从XXL-job路由策略的“服务容错“说起

传送门

分布式定时任务系列10:XXL-job源码分析之路由策略

再回首XXL-job的"故障转移"路由策略

此策略的定义是:按照顺序依次进行心跳检测,第一个心跳检测成功的机器选定为目标执行器并发起调度!这里再看下官网对于它的解释:

一次完整任务流程包括”调度(调度中心) + 执行(执行器)”两个阶段。

  • “故障转移”发生在调度阶段,在执行器集群部署时,如果某一台执行器发生故障,该策略支持自动进行Failover切换到一台正常的执行器机器并且完成调度请求流程。

而XXL-job的"故障转移"路由策略其实就是一种容错性设计!

什么是容错性设计

什么是容错性设计(Design for Failure)呢?放在XXL-job的"故障转移"这个场景下,有几个对应的条件:

  • 针对的是"在执行器集群部署时",换句话说:是在分布式环境中发生的调用,如果是单机部署就不在此讨论范围了
  • 针对的对象是服务调用,但不限于远程服务、内部接口调用,或者是方法等

在微服务大行其道的今天,容错性设计成了不可或缺的一个核心原则。随着服务的拆分,服务的模块和实例都会越来越多,发生错误的概率也比拆分之前要大。

服务的出错、机器节点的崩溃、网络的抖动、远程调用的网状化都会带来更大的出错概率,如果其中某一个服务的崩溃,就导致所有用到这个服务的其他服务都无法正常工作,一个点的错误经过层层传递,最终波及到调用链上与此有关的所有服务, 会产生雪崩效应。或者服务就算不崩溃,但由于处理能力有限,面对突发大量请求时,大部分请求直至超时都无法完成处理(比如典型的tomcat这种一对一的BIO网络模型)。

所以在微服务场景下,必须要考虑各种各样的异常!并且由此诞生出了专门的"服务治理"手段,来提升系统的可用、可靠性。否则采用了微服务构建分布式架构,可用性还不如果原来的单体架构,那不是脱了裤子放屁嘛。由此针对分布式架构下的各种各样的异常的解决方案,统一称为容错性策略:

容错策略,指的是“面对故障,我们该做些什么

容错策略

关于容错策略,要转变的一个观念就是:服务不可能永不出错!要从追求构建永不出错的服务转变为接受服务会出错,并且对服务的各种异常制定应对策略,包括"服务治理"的各种常规手段:注册发现、负载均衡、流量控制、服务质量管理、服务追踪及服务容错等。当然这里除了基础服务以外,也包括系统层面的架构设计,比如阿里去的2次事故,就分别是由软硬件引起的(所以也要正确看待大厂多少个9的神话迷信):

 那么常见的容错策略有哪些呢?第一个肯定就是上面一直出现的"故障转移"

故障转移(Failover)

在生产环境中为了达到服务"高可用"(尤其是那些重要服务,比如所谓的A类、一类)一般的做法就是集群部署,或者叫多副本部署。这些副本可能部署在不同的节点(避免机器宕机,同机房)、不同的网络交换机(避免网络分区,不同机房同城),甚至是不同的地域(避免自然灾害,异步)中。

所谓的故障转移是指,如果被调用的目标服务出现故障,系统不会立即向调用者返回失败结果,而是自动切换到其他服务副本继续发起调用,尝试返回成功调用的结果,从而达到整体的高可用性。

在前面分布式定时任务系列10:XXL-job源码分析之路由策略,里面谈到了用此策略的一些场景及限制:

对于FAILOVER这种故障处理策略来说,不同的框架或者场景实现不同,难易程度也不同,而且也不是所有的系统/接口适合故障转移。比如对一个有超时机制的微服务架构来说:

如果链路比较多,一个业务请求需要经过A->B->C3个服务,每个服务有2个节点。假设在A服务调用B的时候,存在网络问题,A的实例A1失败,这里转移到A2成功;A->B的时候,B1失败,B2成功;同理B->C,C1失败,C2成功,虽然最终请求成功,但是整体耗时会增大一倍,早已经进过了网关(整体)响应时长导致timeout了,所以有些场景的FAILOVER并不一定是有益甚至有害的(重试也增加了服务调用次数,服务的压力)

一句话总结就是:故障转移要配合服务重试,并且要限制重试的调用次数!

快速失败(Failfast)

故障转移要进行服务重试,那如果不进行重试呢?如果不进行重试,并且明确告诉调用者失败结果,让调用者来自行决定下一步处理的方案,称为"快速失败"!

比如在支付场景中,调用了银行的扣款接口,调用成功但是返回时由于网络异常,未成功返回扣款结果。这个时候是很难判断到底扣款成功没有的,如果进行扣款重试,很有可能会造成重复扣款带来一系列问题:用户发现重复扣款肯定要投诉,影响公司品牌形象;业务上还要做退款处理等一系统订正问题。

所以最好的方式就是直接失败,告诉调用方,再配合对应的查询操作来避免重复扣款:

  • 如果回查接口显示扣款成功,展示支付成功
  • 如果回查接口显示失败,提示用户,让他重新发起支持

而且这里有一个前置条件:一般支持重试的接口,都是幂等的。如果一个接口设计的不是幂等的,肯定不能盲目重试

安全失败(Failsafe)

如果不进行重试,并且明确告诉调用者失败结果,让调用者来自行决定下一步处理的方案,称为"快速失败"!比如上面的支付场景,调用银行的扣款接口失败就不能重试。但是如果扣款成功,只是扣款短信发送失败呢?这个时候其实是可以返回用户支付成功的,因为没有短信通知不影响用户的支付,不属于核心业务逻辑:

  • 在业务逻辑中可以划分核心逻辑+旁路逻辑
  • 如果旁路逻辑失败,是影响主逻辑执行,可以视为成功,只需要记录对应的日志即可(甚至记录日志本身失败,也是可以接受)

这种就称之为"安全失败"!

沉默失败(Failsilent)

沉默失败的这种策略不是很常见,因为对开发的要求很高:

如果大量的请求需要等到超时(或者长时间处理后)才宣告失败,很容易因为某个远程服务的请求堆积而消耗大量的线程、内存、网络等资源,进而影响到整个系统的稳定性。

面对这种情况,一种合理的失败策略是当请求失败后,就默认服务提供者一定时间内无法再对外提供服务,不再向它分配请求流量,并将错误隔离开来,避免对系统其他部分产生影响。这种容错策略,就被称为沉默失败

这里还要涉及到类似舱壁隔离模式所以一般的话,在实际微服务中对于Hystrix这种支持服务降级框架,启用了服务降级就可以视为一种沉默失败的例子。

故障恢复(Failback)

"故障恢复",顾名思义就是调用失败了也会明确告知调用者失败结果(故障,出了问题),但是等服务"恢复"了就可以自动执行成功。假设在调用的过程中,服务发生异常,但是通过服务的一些自动恢复手段,通过补偿机制实现最终调用执行成功,就称为"故障恢复"

故障恢复策略的常见实现方式:在内存(或存储中,比如DB表)中有一个队列,当调用失败了就将信息存入队列中,然后由系统自动的发起重试。当然系统发起重试也不一定能成功,这取决于失败原因:如果是业务异常,比如接口参数不正确、账号不对,无论重试多少次都不会成功,只有那些由于网络环境、系统负载过高、流量限流导致的系统异常,才有可能重试成功,所以:

  • 故障恢复伴随着,快速失败策略
  • 故障恢复也必须要求接口幂等
  • 故障恢复也要限制调用次数,同故障转移类似
  • 通过故障恢复策略的场景,一般实时性要求不高,比如支付场景:在扣款完成,发送通知短信的时候,短信服务不可用,会将此笔支付请求记录下来,要等到它恢复时才能重新发送,实时性肯定不高

故障转移、快速失败、安全失败、沉默失败和故障恢复这 5 种容错策略的英文,都是以“Fail”开头的,都是针对调用失败时如何进行弥补的,这是它们的共同点。不同点当然就是应用错误的策略不一样了。

并行调用(Forking)

接下来,并行调用和广播调用这两种策略,则是在调用之前就开始考虑如何获得最大的成功概率。

并行调用策略,是指一开始就同时向多个服务副本发起调用,只要有其中任何一个返回成功,那调用便宣告成功。这种策略是在一些关键场景中,使用更高的执行成本换取执行时间和成功概率的策略。

这种类似"双保险"的思路是很有用的,用更高的执行成本来换取更高的执行成功率。但是这种调用设计其实并不常见,至少不是业务开发的主流模式,因为一般的开发模式(包括设计)都是直接调用依赖的服务接口,(最终表现为单向单次调用),只有极少的场景才会用到它:

  • 假设serviceA->serviceB,serviceB有100个实例,通常情况下serviceB不可用的概率为0.01
  • 那么一次调用,会调用serviceB达100次,其中99次都是没必要的

一句话就是"性价比"太低,所以只有极端的特殊情况下,为了重要的、必要的、成功率要求足够高的、且幂等的场景才可能会有诉求。而且还会涉及到业务端代码的额外开发。

广播调用(Broadcast)

广播调用与并行调用是相对应的,都是同时发起多个调用,但并行调用是任何一个调用结果返回成功便宣告成功,而广播调用则是要求所有的请求全部都成功,才算是成功。

 这种其实也比较少见,广播调用通常被用于实现“刷新分布式缓存”这类的操作。不过很多时候,对分布式缓存这种,一般都是通过redis集中式缓存来取代,避免分布式缓存的操作。如果真的需要本地+远程组成二级缓存,一般也让本地cache自动过期+刷新策略来,容忍一定时间段的不一致来换取简单性,而不是去要求分布式缓存的强一致性

仔细想想,其实并行调用、广播调用就是故障转移的特例:

  • 故障转移,循环被调用服务列表,只要一个可用就表示成功
  • 并行调用,一来就调用服务列表的所有服务,只要一个可用就表示成功
  • 广播调用,一来就调用服务列表的所有服务,但是每一个可用才表示成功

如果XXL-job增加了并行调用、广播调用的路由策略,对应的代码实现应该也不难了...

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值