七、集群容错

集群服务调用失败后,服务框架需要能够在底层自动容错。

1 集群容错场景

在分布式服务框架中,业务消费者不需要了解服务提供者的具体未知,它发起的服务调用请求也不包含服务提供者具体地址信息。因此,某个服务提供者是否可用对消费者而言无关紧要,最终的服务调用成功才是最重要的。
经过服务路由之后,选定某个服务提供者进行远程服务调用,但是服务调用可能会出错,下面进行故障场景进行分析。

1.1 通信链路故障

这里的链路指的是消费者和服务提供者之间的链路(通常为长连接),可能导致链路中断的原因有:

  1. 通信过程中,对方突然宕机导致链路中断。
  2. 通信过程中,对方因为解码失败等原因 Rest 掉连接,导致链路中断。
  3. 通信过程中,消费者 write SocketChannel 发生 IOException 导致链路中断。
  4. 通信过程中,消费者 read SocketChannel 发生 IOException 导致链路中断。
  5. 通信双方因为心跳超时,主动 close SocketChannel 导致链路中断。
  6. 通信过程中,网络发生闪断故障。
  7. 通信过程中,交换机异常导致链路中断。
  8. 通信过程中,消费者或者服务提供者因为长时间 Full GC 导致链路中断。

1.2 服务端超时

  1. 服务端的 I/O 线程没有及时从网络中读取客户端请求消息,导致该问题的原因通过是 I/O 线程被意外阻塞或者执行长周期操作。
  2. 服务端业务处理缓慢,或者被长时间阻塞,例如查询数据库,由于没有索引导致全表查询,耗时较长。
  3. 服务端发生长时间 Full GC,导致所有业务线程暂停运行,无法及时返回应答给客户端。

1.3 服务端调用失败

  1. 服务端解码失败,返回消息解码失败异常。
  2. 服务端发生动态流控,返回流控异常。
  3. 服务端消息队列积压率超过最大阈值,返回系统阻塞异常。
  4. 访问权限校验失败,返回权限相关异常。
  5. 违反 SLA 策略,返回 SLA 控制相关异常。
  6. 其它系统异常。

服务调用异常不包括业务层面的处理异常,例如数据库操作异常、用户记录不存在等异常。

2 容错策略

服务不同,容错策略往往也不同,下面是集群容错和服务路由的关系:

消费者根据配置的路由策略选择某个目标地址之后,发起远程服务调用,发起远程服务调用,在此期间如果发生了远程服务调用异常,则需要服务框架进行集群容错,重新进行选路和调用。集群容错是系统自动执行的,上层用户并不需要关心底层的服务调用过程。

2.1 失败自动切换(FailOver)

服务调用失败自动切换策略指的是当发生 RPC调用异常时,重新选路,查找下一个可用的服务提供者。
服务发布的时候,可以指定服务的集群容错策略。消费者可以覆盖服务提供者的通用配置,实现个性化的容错策略。

FailOver 策略的设计思路如下:消费者路由操作完成之后,获得目标地址,调用通信框架的消息发送接口发送请求,监听服务端应答。如果返回的结果是 RPC调用异常(超时、流控、解码失败等系统异常),根据消费者集群容错的策略进行容错路由,如果是 FailOver,则重新返回到路由 Handler 的入口,从路由节点继续执行。选录完成之后,对目标地址进行对比,防止重新路由到故障服务节点,过滤掉上次的故障服务提供者之后,调用通信框架的消息发送接口发送请求消息。

  • 读操作,因为通常它是幂等的。
  • 幂等性服务,保证调用1 次和 N 次效果相同。
    注意:失败重试会增加服务调用时延,因此框架必须对失败重试的最大次数做限制,通常默认为 3,防止无限制重试导致服务调用时延不可控。

2.2 失败通知(Failback)

适用于非幂等性的服务调用,通过对失败错误码等异常信息的判断,决定后续的执行策略。
Failback 的设计方案如下:服务框架获取到服务提供者返回的 RPC 异常响应之后,根据策略进行容错。将 RPC异常通知给消费者,由消费者捕获异常进行后续处理。

2.3 失败缓存(Failcache)

Failcache 策略是失败自动恢复的一种,应用场景如下:

  1. 服务有状态路由,必须定点发送到指定的服务提供者。当发生链路中断、流控等服务暂时不可用时,服务框架将消息临时缓存起来,等待周期T,重新发送,直到服务提供者能够正常处理该消息。
  2. 对时延要求不敏感的服务。系统服务调用失败,通常是链路暂时不可用、服务流控、GC 挂住服务提供者进程等,这种失败不是永久性的失败,它的恢复是可预期的。如果消费者对服务调用时延不敏感,可以考虑采用自动恢复模式,即先缓存,再等待,最后重试。
  3. 通知类服务。例如通知粉丝积分增长、记录接口日志等,对服务调用的实时性要求不高,可以容忍自动恢复带来的时延增加。

为了保证可靠性,Failcache 策略在设计的时候需要考虑如下几个要素:

  1. 缓存时间、缓存对象上限数等需要做出限制,防止内存溢出。
  2. 缓存淘汰算法的选择,是否支持用户配置。
  3. 定时重试的周期T、重试的最大次数等需要做出限制并支持用户指定。
    重试达到最大上限仍失败,需要丢弃消息,记录异常日志。

2.4 快速失败(Failfast)

在业务高峰期,对于一些非核心的服务,希望只调用一次,失败也不再重试,为重要的核心服务解约宝贵的运行资源。此时,快速失败是不错的选择。
原理在于,获取服务调用异常之后,直接忽略异常,记录异常日志。

2.5 容错策略扩展

无论默认支持多少种容错策略,在业务实际使用过程中都需要支持用户自定义扩展容错策略。

3 个人总结

集群容错虽然功能简单,设计也并不复杂(不复杂???),但是该特性却非常重要。