微服务可靠性设计

转载说明:这篇文章针对微服务的可靠性设计讲的比较全面,我只摘出了其中几个关键点转载,原文参考链接。在故障隔离,容错,降级,熔断机制,流量控制几个方面详细讲解了微服务的可靠性,不错的参考。


背景

微服务化之后,系统分布式部署,传统单个流程的本地API调用被拆分成多个微服务之间的跨网络调用,由于引入了网络通信、序列化和反序列化等操作,系统发生故障的概率提高了很多。

微服务故障,有些是由于业务自身设计或者编码不当导致,有些是底层的微服务化框架容错能力不足导致。在实际项目中,需要从业务和平台两方面入手,提升微服务的可靠性。

无处不在的故障

分布式部署和调用

传统单体架构一个完整的业务流程往往在同一个进程内部完成处理,不需要进行分布式协作,它的工作原理如下所示:
这里写图片描述
图1-1 传统单体架构本地方法调用

微服务化之后,不同的微服务采用分布式集群部署方式,服务的消费者和提供者通常运行在不同的进程中,需要跨网络做RPC调用,它的工作原理如下所示:
这里写图片描述
图1-2 微服务分布式RPC调用

分布式调用之后,相比于传统单体架构的本地方法调用,主要引入了如下潜在故障点:

  • 序列化与反序列化:微服务的请求和应答都需要经过序列化和反序列化,做消息的跨网络通信,由于数据结构不一致、不支持的数据类型、对方编解码错误等都会导致序列化和反序列化失败,进而导致微服务调用失败。
  • 网络问题:常见的包括网络超时、网络闪断、网络单通、网络拥塞等,都可能会导致微服务远程调用的失败。

大型系统微服务进程内合设

理想情况下,每个微服务都独立打包和部署,微服务之间天然就支持进程级隔离,但事实上,对于一个大规模的企业IT系统、或者大型网站,是由成百上千个微服务组成的,在实践中,微服务通常是不可能做到百分之百独立部署的,原因如下:

  1. 方便开发:通常会按照业务域划分团队,同一个业务域往往包含多个微服务,由一个团队负责开发。为了方便CI/CD,同一业务域的微服务往往打包和部署在一起,而不是每个微服务独立打包部署。
  2. 方便运维:海量的微服务进程(以1000个微服务 * 10个进程实例为例),会增加部署、数据采集(性能KPI和日志等)、告警、问题定位等成本,如果运维自动化程度不高,很难支撑大规模的微服务独立部署。
  3. 提升性能:一些业务对时延非常敏感,如果该业务链上的所有微服务调用都跨网络通信,时延往往无法满足业务要求。通过将微服务合设在同一个进程之内,利用路由短路,把RPC调用转化成本地方法调用,可以极大的提升性能。
  4. 简化分布式事务处理:分布式部署之后,会带来分布式事务问题。有时候业务为了简化分布式事务的处理,将事务相关的微服务部署在同一个进程中,把分布式事务转换成本地事务,简化事务处理。

不同的微服务合设在同一个进程之中,就会引入一系列潜在的故障点,例如:

  • 处理较慢的微服务会阻塞其它微服务
  • 某个微服务故障蔓延,可能导致整个进程不可用
  • 低优先级的微服务,抢占高优先级微服务的资源

微服务健康度

传统情况下,往往使用服务注册中心检测微服务的状态,当检测到服务提供者不可用时,会将故障的服务信息广播到集群所有节点,消费者接收到服务故障通知消息之后,根据故障信息中的服务名称、IP地址等信息,对故障节点进行隔离。它的工作原理如下所示:
这里写图片描述
图1-3 微服务状态检测

使用基于心跳或者会话的微服务状态检测,可以发现微服务所在进程宕机、网络故障等问题,但在实际业务中,微服务并非“非死即活”,它可能处于“亚健康状态”,服务调用失败率很高,但又不是全部失败。或者微服务已经处于过负荷流控状态,业务质量受损,但是又没有全部中断。

使用简单的微服务状态检测,很难应对上述这些场景。通过对微服务的运行质量建模,利用微服务健康度模型,根据采集的各种指标对微服务健康度实时打分,依据打分结果采取相应的可靠性对策,可以更有针对性的保障系统的可靠性。

同步的I/O操作

在整个微服务调用过程中,主要会涉及到三类I/O操作:

  • 网络I/O操作,涉及到网络读写
  • 磁盘I/O操作,主要是记录日志、话单、写本地文件等
  • 数据库访问,例如Java使用JDBC驱动进行数据库操作
    这里写图片描述
    图1-4 微服务涉及的主要I/O操作

凡是涉及到I/O操作的,如果I/O操作是同步阻塞模式,例如Java的BIO、文件File的读写操作、数据库访问的JDBC接口等,都是同步阻塞的。只要访问的网络、磁盘或者数据库实例比较慢,都会导致调用方线程的阻塞。由于线程是Java虚拟机比较重要的资源,当大量微服务调用线程被阻塞之后,系统的吞吐量将严重下降。

第三方SDK API调用

在微服务中,调用第三方SDK API,也可能会引入新的故障点,例如通过FTP客户端访问远端的FTP服务,或者使用MQ客户端访问MQ服务,如果这些客户端API的容错性设计不好,也会导致调用方的级联故障,这些故障是潜在和隐性的,在设计的时候往往容易被忽视,但它带来的风险和危害是巨大的。

微服务可靠性

软件可靠性是指在给定时间内,特定环境下软件无错运行的概率。软件可靠性包含了以下三个要素:

1) 规定的时间:软件可靠性只是体现在其运行阶段,所以将运行时间作为规定的时间的度量。运行时间包括软件系统运行后工作与挂起(启动但空闲)的累计时间。由于软件运行的环境与程序路径选取的随机性,软件的失效为随机事件,所以运行时间属于随机变量。

2) 规定的环境条件:环境条件指软件的运行环境。它涉及软件系统运行时所需的各种支持要素,如支持硬件、操作系统、其它支持软件、输入数据格式和范围以及操作规程等。

3) 规定的功能:软件可靠性还与规定的任务和功能有关。由于要完成的任务不同,则调用的子模块就不同(即程序路径选择不同),其可靠性也就可能不同。所以要准确度量软件系统的可靠性必须首先明确它的任务和功能。

关键的可靠性因素

微服务的运行质量,除了自身的可靠性因素之外,还受到其它因素的影响,包括网络、数据库访问、其它相关联的微服务运行质量等。微服务的可靠性设计,需要考虑上述综合因素,总结如下:
这里写图片描述

异步IO操作

本章忽略

故障隔离

由于大部分微服务采用同步接口调用,而且多个领域相关的微服务会部署在同一个进程中,很容易发生“雪崩效应”,即某个微服务提供者故障,导致调用该微服务的消费者、或者与故障微服务合设在同一个进程中的其它微服务发生级联故障,最终导致系统崩溃。

为了避免“雪崩效应”的发生,需要支持多种维度的依赖和故障隔离,以实现微服务的HA。

通信链路隔离

由于网络通信本身通常不是系统的瓶颈,因此大部分服务框架会采用多线程+单个通信链路的方式进行通信,原理如下所示:
这里写图片描述
3-1 多线程-单链路P2P通信模式

正如前面章节所述,由于微服务使用异步非阻塞通信,单个I/O线程可以同时并发处理多个链路的消息,而且网络读写都是非阻塞的,因此采用多线程+单链路的方式进行通信性能本身问题不大。但是从可靠性角度来看,只支持单链路本身又存在一些可靠性隐患,我们从下面的案例中看下问题所在。

某互联网基地微服务架构上线之后,发现在一些时段,经常有业务超时,超时的业务没有固定规律。经定位发现当有较多的批量内容同步、语音和视频类微服务调用时,系统的整体时延就增高了很多,而且存在较突出的时延毛刺。由于这些操作获取的消息码流往往达到数M到数十兆,微服务之间又采用单链路的方式进行P2P通信,导致大码流的传输影响了其它消息的读写效率,增大了微服务的响应时延。

问题定位出来之后,对微服务之间的通信机制做了优化,节点之间支持配置多链路,每个链路之间还可以实现不同策略的隔离,例如根据消息码流大小、根据微服务的优先级等策略,实现链路级的隔离,优化之后的微服务通信机制:
这里写图片描述
图3-2 支持多链路隔离

调度资源隔离

微服务之间隔离

当多个微服务合设运行在同一个进程内部时,可以利用线程实现不同微服务之间的隔离。

对于核心微服务,发布的时候可以独占一个线程/线程池,对于非核心微服务,则可以共享同一个大的线程池,在实现微服务隔离的同时,避免线程过于膨胀:
这里写图片描述
图3-3 微服务之间故障隔离

假如非核心服务3发生故障,长时间阻塞线程池1的工作线程,其它与其共用线程池消息队列的非核心服务1和服务2只能在队列中排队等待,当服务3释放线程之后,排队的服务1和服务2可能已经超时,只能被丢弃掉,导致业务处理失败。

采用线程池隔离的核心服务1和服务2,由于各自独占线程池,拥有独立的消息队列,它的执行不受发生故障的非核心服务1影响,因此可以继续正常工作。通过独立线程池部署核心服务,可以防止故障扩散,保障核心服务的正常运行。

第三方依赖隔离

在微服务中通常会调用第三方中间件服务,例如分布式缓存服务、分布式消息队列、NoSQL服务等。只要调用第三方服务,就会涉及跨网络操作,由于客户端SDK API的封装,很多故障都是隐性的,因此,它的可靠性需要额外关注。

整体而言,第三方依赖隔离可以采用线程池 + 响应式编程(例如RxJava)的方式实现,它的原理如下所示:

1) 对第三方依赖进行分类,每种依赖对应一个独立的线程/线程池。

2) 微服务不直接调用第三方依赖的API,而是使用异步封装之后的API接口。

3) 异步调用第三方依赖API之后,获取Future对象。利用响应式编程框架,可以订阅后续的事件,接收响应,针对响应进行编程。

利用Netflix开源的hystrix + RxJava,可以快速实现第三方依赖的隔离,后续章节我们会详细介绍下如何使用。

进程级隔离

对于核心的微服务,例如商品购买、用户注册、计费等,可以采用独立部署的方式,实现高可用性。
容器隔离

微服务鼓励软件开发者将整个软件解耦为功能单一的服务,并且这些服务能够独立部署、升级和扩容。如果微服务抽象的足够好,那么微服务的这一优点将能够提升应用的敏捷性和自治理能力。

利用Docker容器部署微服务,可以带来如下几个优点:

高效:Docker容器的启动和停止不需要几分钟,只要几百毫秒就足够了。使用Docker部署微服务,微服务的启动和销毁速度非常快,在高压力时,可以实现秒级弹性伸缩。
高性能:Docker容器的性能接近裸的物理机,比VM平均高20%+。
隔离性:利用Docker,可以实现0.1 core的隔离。基于细粒度的资源隔离机制,可以实现高密度的部署微服务,同时实现它们之间的资源层隔离,保障微服务的可靠性。
可移植性:在基于虚拟机的解决方案中,应用的可移植性通常来说会受到云提供商所提供的虚拟机格式限制。如果应用程序需要部署到不同类型的虚拟机中,需要针对特定的虚拟机格式做镜像文件,新增很多额外的开发和测试工作量。Docker容器的设计理念是“一次编写,到处运行”,这可以使开发者避免上面这种限制。
基于Docker容器部署微服务,实现物理资源层隔离示意图如下所示:
这里写图片描述
图3-4 基于Docker容器的微服务隔离

VM隔离

除了Docker容器隔离,也可以使用VM对微服务进行故障隔离,相比于Docker容器,使用VM进行微服务隔离存在如下优势:

微服务的资源隔离性更好,CPU、内存、网络等可以实现完全的资源隔离。
对于已经完成硬件虚拟化的遗留系统,可以直接使用已有的VM,而不需要在VM中重新部署Docker容器。

服务降级

大促或者业务高峰时,为了保证核心服务的SLA,往往需要停掉一些不太重要的业务,例如商品评论、论坛或者粉丝积分等。

另外一种场景就是某些服务因为某种原因不可用,但是流程不能直接失败,需要本地Mock服务端实现,做流程放通。以图书阅读为例,如果用户登录余额鉴权服务不能正常工作,需要做业务放通,记录消费话单,允许用户继续阅读,而不是返回失败。

通过服务治理的服务降级功能,即可以满足上述两种场景的需求。

强制降级

当外界的触发条件达到某个临界值时,由运维人员/开发人员决策,对某类或者某个服务进行强制降级。

强制降级的常用策略:

不发起远程服务调用,直接返回空。例如mock = force: return null。
不发起远程服务调用,直接抛出指定异常。例如mock = force: throw Exception。
不发起远程服务调用,直接执行本地模拟接口实现类。mock = force: execute Bean: 。

容错降级

当非核心服务不可用时,可以对故障服务做业务逻辑放通,以保障核心服务的运行。

容错降级与屏蔽降级的主要差异是:

触发条件不同:容错讲解是根据服务调用结果,自动匹配触发的;而屏蔽降级往往是通过人工根据系统运行情况手工操作触发的。
作用不同:容错降级是当服务提供者不可用时,让消费者执行业务放通;屏蔽降级的主要目的是将原属于降级业务的资源调配出来供核心业务使用。
调用机制不同:一个发起远程服务调用,一个只做本地调用。
容错降级的常用策略如下:

异常转义:mock = fail: throw Exception。
自定义降级逻辑:mock = fail: execute Bean: 。将异常屏蔽掉,直接执行本地模拟接口实现类,返回Mock接口的执行结果。

服务降级Portal

利用服务治理Portal,可以在线的动态修改微服务的降级策略,实时生效,它的界面如下所示:
这里写图片描述
图4-1 服务降级配置界面

熔断机制

熔断机制(Circuit Breaker),也叫自动停盘机制,是指当股指波幅达到规定的熔断点时,交易所为控制风险采取的暂停交易措施。

在微服务领域,熔断机制是从消费端保护微服务提供者的措施,当微服务的运行质量低于某个临界值时,启动熔断机制,暂停微服务调用一段时间,以保障后端的微服务不会因为持续过负荷而宕机。

工作原理

微服务的熔断机制原理如下所示:

  1. 微服务调用时,对熔断开关状态进行判断,当熔断器开关关闭时, 请求被允许通过熔断器。如果当前微服务健康度高于指定阈值, 开关继续保持关闭。否则开关切换为打开状态。
  2. 当熔断器开关打开时,微服务调用请求被禁止通过。调用失败,执行本地降级逻辑,如果没有实现降级逻辑,默认返回异常。
  3. 当熔断器开关处于打开状态时, 经过指定周期T, 熔断器会自动进入半开状态, 这时熔断器会允许请求通过,当请求调用成功时, 熔断器恢复到关闭状态。若失败, 则继续保持打开状态。
    它的工作原理示意如下:
    这里写图片描述
    图4-2 微服务熔断器工作原理

熔断器机制能保证微服务消费者在微服务运行状态不佳时,快速返回结果,避免大量的同步等待。并且能在指定周期T后继续侦测微服务是否可用, 以实现故障恢复之后的自动感知。

微服务健康度

熔断器开关的状态取决于微服务的运行质量,微服务的运行质量通常由多种因素决定,具有多个衡量因子。通过对微服务健康度建模,可以实现对微服务运行质量的360°实时评估。

微服务健康度模型如下所示:
这里写图片描述
图4-3 微服务健康度模型

微服务运维体系通过分布式日志采集系统、告警系统、性能KPI数据采集等,利用在线大数据实时分析技术,通过健康度模型,对微服务的健康度按照周期进行实时打分,同时将微服务的得分通过消息队列订阅发布出去,各个节点订阅微服务的健康度得分,与熔断器阈值进行比较,修改熔断器开关的状态。

流量控制

当资源成为瓶颈时,服务框架需要对消费者做限流,启动流控保护机制。流量控制有多种策略,比较常用的有:针对访问速率的静态流控、针对资源占用的动态流控等。

在实践中,各种流量控制策略需要综合使用才能起到较好的效果。

动态流控

动态流控的最终目标是为了保命,并不是对流量或者访问速度做精确控制。当系统负载压力非常大时,系统进入过负载状态,可能是CPU、内存资源已经过载,也可能是应用进程内部的资源几乎耗尽,如果继续全量处理业务,可能会导致消息严重积压或者应用进程宕机。

动态流控检测的资源包括:

CPU使用率。
内存使用率(对于Java,主要是JVM内存使用率)。
队列积压率。
主机CPU、内存使用率采集算法非常多,例如使用java.lang.Process执行top、sar等外部命令获取系统资源使用情况,然后解析后计算获得资源使用率。也可以直接读取操作系统的系统文件获取相关数据,需要注意的是,无论是执行操作系统的本地命令,还是直接读取操作系统的资源使用率文件,都是操作系统本地相关的,不同的操作系统和服务器,命令和输出格式可能存在很大差异。在计算时需要首先判断操作系统类型,然后调用相关操作系统的资源采集接口实现类,通过这种方式就可以支持跨平台。

动态流控是分级别的,不同级别拒掉的消息比例不同,这取决于资源的负载使用情况。例如当发生一级流控时,拒绝掉1/4的消息;发生二级流控时,拒绝掉1/2消息;发生三级流控时,所有的消息都被流控掉。

不同的级别有不同的流控阈值,系统上线后会提供默认的;流控阈值,不同流控因子的流控阈值不同,业务上线之后通常会根据现场的实际情况做阈值调优,因此流控阈值需要支持在线修改和动态生效。

需要指出的是为了防止系统波动导致的偶发性流控,无论是进入流控状态还是从流控状态恢复,都需要连续采集N次并计算平均值,如果连续N次平均值大于流控阈值,则进入流控状态;同理,只有连续N次资源使用率平均值低于流控阈值,才能脱离流控恢复正常。

静态流控

静态流控主要针对客户端访问速率进行控制,它通常根据服务质量等级协定(SLA)中约定的QPS做全局流量控制,例如计费服务的静态流控阈值为200 QPS,则无论集群有多少个计费服务实例,它们总的处理速率之和不能超过200 QPS。

由于微服务具备弹性伸缩、动态上线和下线等特性,因此集群中某个微服务实例的节点个数是动态变化的,采用传统的平均分配制无法做到精准的控制。

在实践中,比较成熟的集群静态流控策略是动态配额申请制,它的工作原理如下:

  1. 系统部署的时候,根据微服务节点数和静态流控QPS阈值,拿出一定比例的配额做初始分配,剩余的配额放在配额资源池中。
  2. 哪个微服务节点使用完了配额,就主动向服务注册中心申请配额。配额的申请策略:如果流控周期为T,则将周期T分成更小的周期T/N(N为经验值,默认值为10),当前的服务节点数为M个,则申请的配额为 (总QPS配额 - 已经分配的QPS配额)/M * T/N。
  3. 总的配额如果被申请完,则返回0配额给各个申请配额的服务节点,服务节点对新接入的请求消息进行流控。

用户自定义流控机制

不同的业务,存在不同的流控策略,例如基于微服务优先级的流控、基于节假日的流控、基于业务字段的流控等。底层的服务框架无法实现所有业务级的定制流控策略,因此,过于业务化的流控往往由业务通过自定义流控机制定制实现。

服务框架提供服务调用入口的拦截点和切面接口,由业务实现自定义流控。也可以提供基础的流控框架,供业务实现流控条件判断、流控执行策略等,简化业务的定制工作量。


【原文地址】微服务可靠性设计

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值