2019.03.21 丨 网易新闻技术团队

网易新闻微服务改造之路(微服务组件介绍)

2019.03.21 丨 网易新闻技术团队

微服务(MicroServices)从发展到成熟,已经经历了很长一段时间,它是一种架构概念,业界都在使用微服务去改造目前的单体服务,为用户提供更敏捷、更快速、更安全的服务,基于微服务概念而产生的框架和工具如雨后春笋般的发展起来了,Spring Cloud、Netflix、Dubbo、Consul、Eureka等服务注册中心、服务调度框架都是我们非常熟悉的框架和工具。


在一个组织和企业内部,每个团队依据各自业务的特点及人员的特点,都在使用着不同的服务框架和工具。对于一个组织或企业来说,每个团队都使用不同的服务框架,就会增加人力学习成本、增加资源投入、增大故障发生机率、延长故障处理时间,不利于整个企业的技术发展,需要有一个统一的、稳定的、可维护的基础框架来支撑每个业务的发展。


在网易传媒技术内部,有的团队使用Spring Cloud,有的团队使用Dubbo,有的团队使用Spring MVC,在语言栈上,传媒技术大部分都使用Java语言栈,有部分团队使用C++、Python等其他语言栈,如何使每个团队的服务框架做到统一,而且对现有代码不会造成大量修改,同时也要兼容不同语言栈。如何能平滑、全覆盖、快速的进行微服务改造,是我们面对的比较大的挑战,我将通过两篇文章为大家介绍在传媒技术内部如何进行微服务改造的,本篇文章主要对微服务及目前开源组件做一些介绍,下一篇文章将阐述在传媒内部如何进行微服务改造工作。


一、微服务介绍



在介绍微服务之前,先来了解下单体应用,单体应用就是将应用程序的所有功能都打包成一个独立可运行的程序,可以是JAR、WAR、EAR或其他格式的包,在单体应用的时代,不涉及到分布式部署,不涉及到扩容、持续集成等方面的问题,但随着业务越来越复杂,对快速迭代的需求越来越多,团队的不断壮大,业务请求量越来越大,单体应用的缺点逐渐被暴漏出来,主要包括:


1. 版本管理难、代码维护难、技术债务越积越多:当项目规模越来越大,代码越来越多,涉及到的版本也越来越多,版本的管理越来越复杂,代码很容易产生冲突,随着时间的推移,人员的更迭,增加了很多的技术债务

2. 稳定性差:单个模块出现的问题,会影响到整个应用,导致整个应用都无法提供服务

3. 不够灵活:对应用程序的任何细微修改都会涉及到应用的重新构建、重新部署,开发人员需要等到整个应用程序重新部署完成后才能看到变化。如果多个开发人员共同开发一个应用程序,那么还要等待其他开发人员完成了各自的开发。这降低了团队的灵活性和功能交付频率。

4. 不利于持续集成:单体应用一般比较大,构建和部署的时间都比较长,不利于频繁打包、频繁部署

5. 受限于技术栈:单体应用只能使用统一的技术栈,而且要使用类似的工具,无法依据不同的场景选择不同的技术栈


正是由于单体应用不能满足业务快速发展、敏捷性、灵活性和可扩展性的需求,迫切需要一种更加快速高效的软件交付方式。2014年,Martin Fowler在其博文中发表了“Microervices”一文,首先提到了“微服务”这个词。简单来说,微服务首先是一种架构风格,它将单体应用拆分为更多的小型服务,这些小型服务在独立的进程中运行,服务之间通过HTTP的RESTFul API进行通信,被拆成的每一个小型服务都具有如下特点


1. 单一职责,接口简单:每个服务只负责整个应用单独一部分功能,做到低耦合、高内聚

2. 可以独立部署、升级、扩展或替换:每个服务都可以独立部署,不影响整个系统,从而使服务很容易升级、扩展或替换

3. 可以使用多种语言:每个服务的实现细节与其他服务都没有关系,团队可以根据每个服务的的特点选择最合适的开发语言、数据存储、工具及方法

4. 轻量级通信:服务之间的通信使用轻量级的REST进行通信,异步通信可以选择MQ进行通信


看上去微服务能够解决软件开发方面的很多问题,但同时,微服务也带来了一些其他的问题,主要包括如下几点


1. 系统的复杂度增大,运维的复杂度增大:使用微服务架构,系统由多个独立运行的微服务组成,本身是一个分布式系统,服务之间的通信,服务的可靠性都需要考虑,这都增大了系统复杂度及运维复杂度。在数据库方面,不像单应用只需要考虑一个数据库即可,在微服务下,每个微服务有可能都连着不同的数据库,就需要考虑分布式事务。在部署方面,在微服务下,需要考虑服务的依赖和部署顺序,这都增加了运维方面的复杂度

2. 合理的拆分微服务:微服务的主要目的就是有效拆分应用,实现敏捷开发和部署。如何衡量服务的拆分力度是合理的,这对架构师及团队提出了不小的考验

3. 测试的复杂度增大:在单体应用下,测试人员只需要部署一个应用就可以进行测试,但在微服务下,需要考虑整个服务的调用链路,服务之间的依赖关系,需要定位到出现错误的服务,这些都对测试人员造成了不小的挑战


二、微服务基础组件介绍



在实施微服务过程中,需要有一些基础的组件和服务来支持微服务的实施,以下对一些基础的服务和组件做一些介绍。


1. 服务注册中心


在整个微服务架构中,注册中心是最基础的核心服务之一,它记录着服务和服务地址的映射关系,为服务提供方提供注册、注销功能,为服务消费方提供服务发现的功能。


1). CAP

在讨论注册中心之前,先了解一下CAP原则,它是指在一个分布式系统中所拥有的三个特性:Consistency(一致性)、Availability(可用性)、Partition Tolerance(分区容错性) 。

  • 一致性:它要求在同一时刻点,分布式系统中的所有数据备份都处于同一状态。

  • 可用性:在系统集群的一部分节点宕机后,系统依然能够响应用户的请求。

  • 分区容错性:在网络区间通信出现失败,系统能够容忍。

  • 这三个分布式特性中,不可能同时存在,需要依据实际业务需求进行取舍,目前存在的注册中心实现中,有的实现了AP,有的实现了CP,我们接下来分别进行讨论


2). Eureka

Eureka是由Netflix开源的服务注册中心项目,基于Java语言开发,目前开源了1.X版本,2.X版本已经宣布闭源。Eureka满足了AP,采用了Server/Client模式进行设计。Server是注册中心,为Client提供服务注册和发现的功能,维护着注册到自身的Client相关的信息,提供接口给Client获取服务注册相关的信息。Client将自己的服务信息登记到Server上,并维护自己信息的一致性。


image.png

  • Eureka Server:提供服务的注册和发现功能

  • Provider:将自身的服务注册到Eureka Server中

  • Consumer:从Eureka Server中获取想要的服务列表


Eureka的整体架构图如下所示:

       

image.png

  • Eureka Server:服务注册中心,保存着服务与服务实例之间的关系,提供注册、下线、查询等服务

  • Application Client:服务消费者角色,向服务注册中心获取服务实例列表,并远程调用一个服务实例的接口

  • Application Service:服务提供者的角色,向Eureka Server注册和更新自己的信息,同时能从Eureka Server的注册表中获取到其他服务的信息

Eureka保证了AP,没有保证不同集群之间的Server数据强一致性,仅通过数据拷贝的方式争取注册中心数据的最终一致性,虽然放弃数据强一致性但是换来了Server的可用性,降低了注册的代价,提高了集群运行的健壮性


3). Consul

Consul是由HashiCorp基于Go语言开发的支持多数据中心分布式高可用的服务发布和注册服务软件,保证了CP(采用Raft算法保证服务的一致性),且支持多种健康检查方式,保证服务提供方的服务是可用

Consul主要支持以下特性:

  • 服务注册与发现:Consul做为服务注册中心,提供服务的注册、注销、服务发现的功能,可以通过Rest Api的方式访问这些接口

  • 健康检查:Consul提供健康检查功能,服务提供方将健康检查地址注册到Consul,Consul在指定时间进行健康检查,并标记有问题的服务实例

  • KV存储:Consul除了提供服务注册中心外,还提供KV存储的功能,可以将服务实例的一些特殊属性值通过KV的形式存储到Consul中

  • 多数据中心:Consul提供多数据中心的支持,数据中心之间通过“Gossip“协议


4). Zookeeper

  • Zookeeper是由Google最早开源的分布式协调服务,最早是和Hadoop、Hbase同时发布,做为Hadoop、Hbase的重要组件,提供了数据/发布订阅、负载均衡、分布式同步等功能

    Zookeeper保证了CP,具体由以下几部分组成:

    • Leader-Server:Leader负责进行投票的发起和决议,更新系统中的数据状态,每个集群只有一个Leader-Server

    • Server:Server中存在Follower和Observer两种类型,其中Follower接受客户端的请求并返回结果(事务请求将转发给Leader处理),并在选举过程中参与投票,Observer与Follower功能一致,但是不参与投票过程,它的存在是为了提高系统的读取速度

    • Client:请求发起方,Server和Client之间可以通过长连接的方式进行交互

    服务注册中心有很多的可选工具,需要依据各自的业务及要求进行选择,传媒依据自己的业务特点,使用了Consul做为服务注册中心,后期将做详细介绍


2. 服务的注册与调用


微服务由一些职责单一的细粒度服务组成分布式网状结构,服务之间通过轻量级协议进行通信。服务提供方需要注册服务,通知消费方,服务消费方需要找到服务提供方实例地址,并进行消费,服务注册中心还需要检查服务提供方的实例的健康状态。

我们将服务消费主要分为以下三类


  • 集中式:在服务提供者和服务消费者之间通过负载均衡进行转发,这个负载均衡比较常见的有硬负载(如F5,现在比较少见),软负载(LVS、HAProxy等软件),负载均衡上保存所有服务的地址映射表,这些地址映射表通常由运维人员进行注册,当服务消费方消费服务时,向负载均衡发出请求,负载均衡依据相应策略(比如:轮询,随机等策略)将请求转发到目标服务,从而实现服务的调用。负载均衡一般都有健康检查机制,会定期摘除不健康的服务实例

      image.png

集中式的优点及缺点如下

优点:实现简单,也容易做集中式的访问控制,目前这种方式还是主流

缺点:最主要的问题就是单点问题,所有的流量都经过负载均衡,当请求量比较大的时候,负载均衡就成为了瓶颈。如果负载均衡发生故障,那对于整个系统的访问是灾难性的。负载均衡在服务调用方和服务提供方之间增加了一层,有一定的性能开销


  • 进程内:将负载均衡以二方包的形式提供给服务消费者和服务提供者,负载均衡在服务消费者及服务提供者的进程中运行,这种方式在很多Java的RPC框架中见到,比如:Dubbo、Ribbon、Motan等

image.png

进程的优点及缺点如下

优点:服务直接调用,没有额外开销,性能比较好,也没有点单故障的问题

缺点:对于不同的语言栈需要开发不同的二方包,维护及开发成本极高。涉及到二方包的升级及变更,都会导致应用的重新部署,这对升级和推广都造成了比较大的阻力


  • 独立进程:前面两种方案都存在一些弊端,为了解决前面两种的问题,提出了一个独立进程负载均衡方案,这个方案是对前两种方案的不足提出的一种折中方案,将第二种方案做了优化,将进程内的负载均衡移了出来,变成了一个独立进程,服务消费方消费服务时,都通过本机的服务代理进行访问


image.png

独立进程优点及缺点如下

优点:是一个分布式方案,一个进程故障只影响该主机上的调用,对整体调用不受影响。服务消费方通过本机的代理进行消费,性能相对较好。没有语言栈的约束,在升级或推广中也会比较容易

缺点:部署相对复杂,中间的环节比较多,排查问题不是很方便


目前在传媒内部,使用的是第二和第三种方案的结合,对于Java语言栈,一般使用进程内方案,性能会比较好,对于其他语言,使用第三中方案,后期会详细介绍。


3. 服务容错


在整个系统微服务化之后,服务与服务之间存在着错综复杂的调用关系,每个服务都不可能保证100%的可靠,服务可能会出现不可用的状况,如果调用方所依赖的服务产生故障,不能快速进行容错或隔离,那个这个应用就会存在拖垮整个应用的风险,如果多个调用方都产生不可用情况,就会产生雪崩,严重时整个系统将不能提供正常服务。


  • Hytrix

        Hytrix是Netflix公司开源的服务容错框架,它通过三个方面解决服务容错相关的问题

    • 隔离:限制调用服务的资源使用,一个服务出现故障不影响其他服务的调用,隔离有线程池隔离模式和信号量隔离模式

      • 线程池隔离模式:使用一个线程池来存储当前的请求,线程池对请求作处理,设置任务返回处理超时时间,堆积的请求堆积入线程池队列

      • 信号量隔离模式:使用一个原子计数器(或信号量)来记录当前有多少个线程在运行,请求来先判断计数器的数值,若超过设置的最大线程个数则丢弃改类型的新请求,若不超过则执行计数操作请求来计数器+1,请求返回计数器-1

    • 降级:资源不足,请求超时或熔断后自动触发降级,配合降级接口返回结果作为兜底,也就是调用fallback,返回一个缺省值

    • 熔断:当失败率达到阈值,自动触发熔断,熔断类似于我们家里的电闸,当电流过载时,会触发电闸断电,保护家里的电器设备一样,当访问量过载时,熔断就会触发,保护我们的接口不会被拖垮

      正常状态下,熔断器处于Closed状态,如果调用超时或持续出错,就会进入熔断状态(Open),后续所有请求都会被拒绝,一段时间后,保护器会尝试进入半熔断状态(Half-Open),允许少量请求进来尝试,如果调用仍然失败,则回到熔断状态,如果调用成功,则回到电路闭合状态


4. 网关


在微服务场景下,一个系统有可能被拆为多个微服务,每个微服务又暴漏了若干API(以Restful形式暴漏),管理起来非常的不方便,接入也非常的麻烦,在外网访问每个服务的时候,需要有一个API网关提供统一的入口,将流量转发给对应的后端取得数据后再一次返回,除此,还需要增加限流、熔断、鉴权等通用功能。


统一网关有如下优点

  • 统一API入口、隔离后端:客户端统一使用网关的入口地址,不需要和后端服务一个个进行交互

  • 认证鉴权:网关提供统一的认证,鉴权服务

  • 负载均衡:网关提供负载均衡算法

  • 降低后端开发对API安全性的考虑:安全统一由网关考虑,后端无需考虑安全

  • 缓存:可以提高接口响应速度

  • 日志:提供统一的接口访问日志

  • 统计:基于接口层面的统计分析

  • 服务降级、流量控制:网关可以做到熔断、降级等功能

  • 安全、防刷:网关提供统一的安全机制

目前开源网关方案有很多,我们列举了如下几个网关产品


image.png

目前在网易传媒使用Kong做为统一网关。

以上对微服务做了一些比较全面的介绍,下一篇会结合网易传媒的实际情况,如何在传媒内部做到微服务框架的落地与推广的。


文章来源:网易新闻技术团队微信公众号,欢迎大家前去关注!

文章地址:网易新闻微服务改造之路(微服务组件介绍)

媒体联系

票务咨询:赵丹丹 15802217295

赞助咨询:郭艳慧 13043218801

媒体支持:景    怡 13920859305



推荐课程

推荐课程

提交需求