我们在分布式开发中经常听到的一个词就是“服务治理”。在理解“服务治理”的概念之前让我们先理解什么是分布式系统,分布式系统之间如何通过RPC(Remote Procedure Call,远程过程调用)方式通信,以及如何解决RPC框架存在的问题,这样才能真正地理解服务治理的核心思想。
分布式系统指的是通过网络连接让多台计算机协同解决单台计算机所不能解决的计算、存储等问题,多台计算机之间通过 RPC 方式通信。在使用分布式系统前,首要解决的问题是如何拆解当前面临的问题。通过使用多台计算机分布式解决问题,让分布式系统中的每台机器都负责解决原问题的一个子集。一般来说,可以使用横向拆分法或者纵向拆分法对复杂的系统进行拆分。
◎横向拆分:在无状态系统中多部署几个实例,通过负载均衡方式协调每个实例所负载的计算量。
◎纵向拆分:将一个大应用拆分为多个小应用(例如,将系统拆分为用户、商品、订单服务),每个小应用都负责处理一部分业务。
然而,虽然通过拆分法解决了计算或存储的问题,但是使用分布式技术进行开发会引发比单体应用更多的问题,比如网络异常、数据一致性及分布式系统性能等。因此,在使用分布式架构开发系统前,需要先深入理解分布式系统的概念和可能存在的异常。
◎服务器宕机:服务器宕机是分布式架构下最常见的异常之一。任何服务器都有可能发生故障,而且故障发生的类型、时间都不尽相同。所以,分布式系统一般允许部分服务器发生故障,但要求在部分服务器发生故障时不影响整个系统的正常使用。
◎网络异常:服务器与服务器之间通过网络通信,若在通信过程中出现消息丢失,则两个节点之间无法进行通信,会出现网络分化、消息乱序等网络问题。
◎分布式系统的三态:如果某个节点向另一个节点发起RPC请求,比如节点A向节点B发送一个消息,节点B根据收到的消息完成某些操作,并将操作的结果通过消息返回给节点A,那么这个RPC请求的执行结果可能有三种状态:成功、失败、超时(未知)。我们将这三种状态称为分布式系统的三态。在设计架构时需要考虑成功、失败、超时(未知)这三种状态的处理方式。
◎存储的数据丢失:对于有状态节点来说,数据丢失意味着状态丢失。通常只能从其他节点读取、恢复该存储数据的状态。
分布式系统的副本指的是在分布式系统中为数据或服务提供的冗余。该副本可分为服务副本和数据副本两种类型。
◎服务副本:多个节点提供某种相同的服务,这种服务不依赖本地节点的存储状态,是一种无状态服务。
◎数据副本:在不同的节点上持久化同一份数据。当出现某一个节点存储的数据丢失时,可以从其他副本上读取该数据。数据多副本是分布式系统解决数据丢失异常的唯一方法,因为数据被分散或者复制到不同的机器上,所以如何保证各台主机之间数据的一致性,成为一个难点。
对于分布式系统而言,服务副本非常容易控制,由于服务本身具备无状况特性,运维人员可以动态增加或者减少服务副本的数量,而不会影响服务接口返回数据的正确性。数据副本分布在不同的计算机上,从技术角度来看,数据的一致性面临着巨大的挑战。数据副本的一致性通常具有以下几种情况。
◎强一致性:任何时刻任何用户或节点都可以读到最近一次成功更新的副本数据。这是程度最高的一致性要求,也是实践中最难实现的一致性。
◎弱一致性:系统并不保证进程或者线程在任何时刻访问数据都会返回最新的更新过的值。系统在数据成功写入之后,不承诺立即读到最新写入的值,也不承诺最终多久之后可以读到最新值。
◎最终一致性:数据一旦更新成功,各个副本上的数据最终将达到完全一致的状态,但需要一定的时间。
然而,分布式系统也存在一些复杂特性,比如分布式系统的三态性、异构性、透明性、并发性、可扩展性等。我们在应用分布式系统的过程中要仔细斟酌这些特性的优势和副作用。
◎异构性:由于分布式系统基于不同的网络、操作系统、计算机硬件和编程语言,因此必须考虑采用一种通用的网络通信协议来屏蔽异构系统之间的差异。开发人员一般选择中间件来屏蔽这些差异。
◎透明性:分布式系统中任意组件的故障及主机的升级或迁移,对用户来说都是透明的。
◎并发性:应用分布式系统的目的是更好地共享资源,所以系统中的每个资源在并发环境下都必须是安全的。
◎可扩展性:随着业务量的增加,系统必须具备可扩展性,以应对因业务量增长而增加的外部流量。
◎故障独立性:任何计算机都有可能发生故障,而且各计算机发生的故障类型不尽相同,发生故障的时间也各不相同。所以,分布式系统一般允许发生部分故障,而不影响整个系统的正常使用。
◎数据一致性:因为数据被分散或者复制到不同的机器上,所以需要保证各台服务器之间数据的一致性。
◎负载均衡:由于分布式系统是多机协同工作的系统,因此为了提高系统的整体效率和吞吐量,必须考虑最大化地发挥每个节点的作用,以最大化地利用资源,避免某个节点过载或者浪费资源。
◎系统的性能:系统每秒的事务处理能力,通常用TPS(Transactions Per Second)来衡量。
◎系统的可用性:系统在面对各种异常时可以正确提供服务的能力。该指标可以用系统停服的时间与正常服务时间的比例来衡量,也可以用某功能的失败次数与成功次数的比例来衡量。系统的可用性是分布式系统的重要指标,是系统容错能力的体现。
◎系统的可扩展性:分布式系统通过扩展集群的机器规模来提高系统性能(增大接口吞吐量、降低接口延时、增大接口并发量)、存储容量、计算能力的特性。
RPC(Remote Procedure Call,远程过程调用)是一种进程间通信方式,也是一种技术思想。使用 RPC 技术时,允许本地程序通过网络调用另一台服务器上的函数或者方法,具体调用过程一般由 RPC 框架实现,不用编码实现。即无论是调用本地函数还是调用远程函数,我们编写的调用代码在本质上基本相同。
1.RPC框架的工作原理
RPC框架要向服务调用方和服务提供方屏蔽各类复杂性操作,比如负载均衡、序列化和反序列化、网络重试、超时等,主要由客户端、服务器端和注册中心3种角色构成,整体架构如图3-1所示。
◎客户端(Client):调用远程服务的服务消费方。客户端调用远程服务就像调用本地函数一样,客户端负责序列化、反序列化、连接池管理、负载均衡、故障转移、超时管理、异步管理等。
◎服务器端(Server):暴露服务的服务提供方。服务器端如同实现一个本地函数一样来实现远程服务提供,服务器端需要做收发包队列、I/O线程、工作线程、序列化及反序列化等工作。
◎注册中心:服务注册与发现的注册中心。
2.RPC调用说明
一次RPC调用流程主要由5部分组成,分别是客户端、客户端存根、服务器端存根、服务提供端和网络传输,其调用流程如图3-2所示。
◎客户端:服务调用方。
◎客户端存根:用于存放服务器端的地址信息,将客户端的请求参数等信息打包成网络消息,再通过网络传输发送给服务器端。
◎服务器端存根:接收客户端发送过来的请求消息并解包,然后调用本地服务处理。
◎服务提供端:服务的真正提供者。
◎网络传输:底层数据传输,可以是TCP或HTTP。
单一应用架构
当网站流量很小时,只需一个应用,将所有功能都部署在一起,以减少部署节点和成本。
此时,用于简化增删改查工作量的 数据访问框架(ORM) 是关键。
垂直应用架构
当访问量逐渐增大,单一应用增加机器带来的加速度越来越小,将应用拆成互不相干的几个应用,以提升效率。
此时,用于加速前端页面开发的 Web框架(MVC) 是关键。
分布式服务架构
当垂直应用越来越多,应用之间交互不可避免,将核心业务抽取出来,作为独立的服务,逐渐形成稳定的服务中心,使前端应用能更快速的响应多变的市场需求。
此时,用于提高业务复用及整合的 分布式服务框架(RPC) 是关键。
流动计算架构
当服务越来越多,容量的评估,小服务资源的浪费等问题逐渐显现,此时需增加一个调度中心基于访问压力实时管理集群容量,提高集群利用率。
此时,用于提高机器利用率的 资源调度和治理中心(SOA) 是关键。实际上这样是一种云架构,实现了服务的动态扩展,在高并发情况下,服务端可以快速部署机器,而对于应用端没有任何其他影响
查阅的一些资料,目前的业界一些比较成熟的解决方案如下:
名称 | 所属公司 | 是否开源 | 资料文档 | 备注 |
Dubbo | 阿里巴巴 | 是 | 多 | |
HSF | 阿里巴巴 | 否 | 中 | 目前已作为阿里云产品EDAS其中的套件开放使用 |
Tars | 腾讯 | 是 | 中 | 已作为腾讯云应用框架对外提供使用 |
JSF | 京东 | 否 | 少 | |
Linkerd | CNCF | 是 | 少 | 原型是Twitter所构建的一个基于scala的可扩展RPC系统Finagle |
Motan | 新浪微博 | 是 | 中 | |
istio | 谷歌、IBM、Lyft | 是 | 少 |
相关资料文档较为丰富的只有一个Dubbo。下面先罗列一下这些解决方案的架构设计(点击图片可跳转到图片出处)。
1.阿里 - Dubbo
2.阿里 - HSF
3.腾讯 - Tars
4.JSF
5.CNCF - Linkerd
6.新浪 - Motan
7.istio
大家可以看到,大部分(Linkerd除外、MSEC没找到架构图)方案的设计风格非常相似,都是通过库的方式在调用客户端做的服务发现。那么除了实际的RPC调用之外,主要多了这3个动作:注册、订阅、变更下发。除了这3个核心动作之外,其它的辅助操作还有统计上报、鉴权等等,这也是我们搭建一个服务治理框架需要实现的功能。从MVP的角度来说,注册、订阅、变更下发是最基础的核心功能。
首先前文里也说了,引入服务治理是为了对整体的RPC调用进行集中化管理。对我们来说其核心价值在于,减少重复劳动、避免手动配置物理文件产生的问题、降低开发人员的技术运用成本。下面针对其中的功能点进行分别讲解。
服务的自动注册:
这是一个服务治理框架的基础功能。大家运用WCF的时候应该感受更加明显,我们要配置一个WCF服务端的时候需要在config文件中做很多配置,甚至大部分公司其实配置都是一模一样的到处复制黏贴,整个这个过程其实是价值较低的重复性劳动。
解决这个问题需要通过动态的感知到服务端的地址信息,然后针对该地址信息进行自动化配置或者模板化配置,让其快速可用。那么这些额外的信息保存在哪,就需要引入一个注册中心的概念来进行集中化管理。
客户端的自动发现:
当我们在config文件中指定具体的IP和端口来定义远程服务的地址,或者直接在程序里硬编码远程服务地址时,本身就是一个端到端的访问方式。无法灵活的在程序运行过程中去增加或减少后端的服务节点。
解决这个问题需要和服务注册的实现方式配套。还可以针对于不同类型的应用制定一些负载均衡的策略进行切换。
变更下发:
客户端的自动发现就依赖于此下发的数据,需要及时把提供服务的节点信息变化下发到各个客户端。它面向的场景如:当我们进行一个发布的时候,先将需要发布的节点从负载均衡列表中移除,然后再进行更新,最后再添加到负载均衡列表中。这个时候避免了访问到正在发布中的程序。
当然这点也可以基于状态检测模块去做,这样可以对服务节点的健康状态感知能力得到更好的加强。
服务治理(SOA governance),按照Anne Thomas Manes的定义是:企业为了确保事情顺利完成而实施的过程,包括最佳实践、架构原则、治理规程、规律以及其他决定性的因素。服务治理指的是用来管理SOA的采用和实现的过程。
业务在刚开始时都是单体应用,随着用户量和访问量的增加,在架构层面会发生变化,逐步由单体应用开发转为分布式应用开发,比如把单体应用中的每个模块都按照特定的方法拆分成一组独立的服务,服务与服务之间通过HTTP或者RPC方式调用。随着业务量的逐步增加,服务的数量也逐步增加。这时维护服务的URL地址就变得非常麻烦,所以需要设计一套系统来统一管理每个服务所对应的URL地址。这套系统就叫作注册中心。当有多个服务时,消费者需要根据规则来调用相关服务,实现软负载均衡,以达到资源利用率最大化的目的。因此,服务注册、服务发现、负载均衡、流量削峰、版本兼容、服务熔断、服务降级、服务限流等方面的问题,都是因服务拆分所引发的一系列问题。如何解决这些问题,让服务更稳定地运行,就叫作服务治理。
总体来说,服务治理指的是企业为了确保事情顺利完成而实施的内容,包括最佳实践、架构原则、治理规程、规律及其他决定性的因素。下面针对服务治理过程中的各个环节做相关说明。
(1)服务:它是分布式架构下的基础单元,包括一个或一组软件功能,其目的是不同的客户端通过网络获取相应的数据,而不用关注底层实现的具体细节。以用户服务为例,当客户端调用用户服务的注册功能时,注册信息会被写入数据库、缓存并发送消息来通知其他关注注册事件的系统,但是调用方并不清楚服务的具体处理逻辑。
(2)注册中心:它是微服务架构中的“通讯录”,记录了服务和服务地址的映射关系,主要涉及服务的提供者、服务注册中心和服务的消费者。在数据流程中,服务提供者在启动服务之后将服务注册到注册中心;服务消费者(或称为服务消费方)在启动时,会从注册中心拉取相关配置,并将其放到缓存中。注册中心的优势在于解耦了服务提供者和服务消费者之间的关系,并且支持弹性扩容和缩容。当服务需要扩容时,只需要再部署一个该服务。当服务成功启动后,会自动被注册到注册中心,并推送给消费者。
(3)服务注册与发布:服务实例在启动时被加载到容器中,并将服务自身的相关信息,比如接口名称、接口版本、IP地址、端口等注册到注册中心,并使用心跳机制定期刷新当前服务在注册中心的状态,以确认服务状态正常,在服务终止时将其从注册表中删除。服务注册包括自注册模式和第三方注册模式这两种模式。
◎自注册模式:服务实例负责在服务注册表中注册和注销服务实例,同时服务实例要发送心跳来保证注册信息不过期。其优点是,相对简单,无须其他系统功能的支持;缺点是,需要把服务实例和服务注册表联系起来,必须在每种编程语言和框架内部实现注册代码。
◎第三方注册模式:服务实例由另一个类似的服务管理器负责注册,服务管理器通过查询部署环境或订阅事件来跟踪运行服务的改变。当管理器发现一个新的可用服务时,会向注册表注册此服务,同时服务管理器负责注销终止的服务实例。第三方注册模式的主要优势是服务与服务注册表是分离的,无须为每种编程语言和架构都完成服务注册逻辑。相应地,服务实例是通过一个集中化管理的服务进行管理的;缺点是,需要一个高可用系统来支撑。
(4)服务发现:使用一个注册中心来记录分布式系统中全部服务的信息,以便其他服务快速找到这些已注册的服务。其目前有客户端发现模式和服务器端发现模式这两种模式。
◎客户端发现模式:客户端从服务注册服务中查询所有可用服务实例的地址,使用负载均衡算法从多个服务实例中选择一个,然后发出请求。其优势在于客户端知道可用服务注册表的信息,因此可以定义多种负载均衡算法,而且负载均衡的压力都集中在客户端。
◎服务器端发现模式:客户端通过负载均衡器向某个服务提出请求,负载均衡器从服务注册服务中查询所有可用服务实例的地址,将每个请求都转发到可用的服务实例中。与客户端发现一样,服务实例在服务注册表中注册或者注销。我们可以将HTTP服务、Nginx的负载均衡器都理解为服务器端发现模式。其优点是,客户端无须关注发现的细节,可以减少客户端框架需要完成的服务发现逻辑;客户端只需简单地向负载均衡器发送请求。其缺点是,在服务器端需要配置一个高可用的负载均衡器。
(5)流量削峰:使用一些技术手段来削弱瞬时的请求高峰,让系统吞吐量在高峰请求下可控,也可用于消除毛刺,使服务器资源的利用更加均衡、充分。常见的削峰策略有队列、限频、分层过滤、多级缓存等。
(6)版本兼容:在升级版本的过程中,需要考虑升级版本后新的数据结构能否理解和解析旧的数据,新协议能否理解旧的协议并做出预期内合适的处理。这就需要在服务设计过程中做好版本兼容工作。
(7)服务熔断:其作用类似于家用的保险丝。当某服务出现不可用或响应超时的情况时,已经达到系统设定的阈值,为了防止整个系统出现雪崩,会暂时停止对该服务的调用。
(8)服务降级:在服务器压力剧增的情况下,根据当前业务情况及流量对一些服务和页面有策略性地降级,以此释放服务器资源,保证核心任务的正常运行。降级时往往会指定不同的级别,面对不同的异常等级执行不同的处理。
(9)服务限流:服务限流可以被认为是服务降级的一种。它通过限制系统的输入和输出流量来达到保护系统的目的。一般来说,系统的吞吐量是可以被测算的。为了保证系统的稳定运行,一旦达到阈值,就需要限制流量。限制措施有延迟处理、拒绝处理或者部分拒绝处理等。
(10)负载均衡策略:它是用于解决一台机器无法处理所有请求而产生的一种算法。当集群里的1台或者多台服务器不能响应请求时,负载均衡策略会通过合理分摊流量,让更多的服务器均衡处理流量请求,不会因某一高峰时刻流量大而导致单个服务器的 CPU或内存急剧上升。
1.交付价值到利益相关者,这是投入与回报的问题
2.对标准和规则的遵从(这是和审计相关的)
3.变更管理:变更一个服务通常会引起不可预见的后果,因为服务的消费者对服务的提供者来说是不可知的。
4.服务质量的保证:弹性添加新服务需要对这些服务给予额外的关注。
服务治理的一些关键活动包括:
1.对开发新服务和升级现有服务的计划
2.管理服务的生命周期:确保升级服务不会影响目前的服务消费者制定方针来限制服务行为:
3.制定所有服务都要遵从的规则,确保服务的一致性
4.监控服务的性能:由于服务组合,服务停机和性能低下的后果是严重的。通过监控服务的性能和可用性,当问题出现的时候能马上采取应对措施。
5.管理由谁来调用服务、怎样调用服务
本文节选自《架构演变实战:从单体到微服务再到中台》这本书