上一篇文章"高并发系统高可用设计方案(一)"我们讨论了,在高并发系统高可用方案设计中,常用的一些架构设计的方法论,除了系统架构方法外,系统中各个子系统之间的交互方式和交互策略也极大的影响着系统的可用性,下面我就来讨论一下优化系统间交互几种方式。
重试
远程服务可能会由于线程阻塞、垃圾回收或者网络抖动,而无法及时返回响应,调用者可以通过重试的方式修复单次调用的故障。需要注意的是,重试是有风险的。比如一个转账操作,第一次请求转账后没有响应,也许仅仅是响应数据在网络中超时了,如果这个时候进行重试,那么可能会导致重复转账,反而造成重大问题。所以,可以重试的服务必须是幂等的。所谓幂等,即服务重复调用和调用一次产生的结果是相同的。有些服务天然具有幂等性,比如将用户性别设置为男性,不管设置多少次,结果都一样。关于系统的幂等方案设计可以参考:分布式系统容错性方案设计:重试与幂等
熔断
重试主要解决偶发的因素导致的单次调用失败,但是如果某个服务器一直不稳定,甚至已经宕机,再请求这个服务器或者进行重试都没有意义了。所以为了保证系统整体的高可用,对于不稳定或者宕机的服务器需要进行熔断。熔断更多的是保护上游服务的可用性。
熔断的主要方式是使用断路器阻断对故障服务器的调用,断路器状态图如下。
断路器有三种状态,关闭、打开、半开。断路器正常情况下是关闭状态,每次服务调用后都通知断路器。如果失败了,失败计数器就 +1,如果超过开关阈值,断路器就打开,这个时候就不再请求这个服务了。过一段时间,达到断路器预设的时间窗口后,断路器进入半开状态,发送一个请求到该服务,如果服务调用成功,那么说明服务恢复,断路器进入关闭状态,即正常状态;如果服务调用失败,那么说明服务故障还没修复,断路器继续进入到打开状态,服务不可用,需要继续等待一个时间窗口,使断路器由打开状态转移到断路器半开状态。
补偿
前面几种方案都是故障发生时如何处理,而补偿则是故障发生后,如何弥补错误或者避免损失扩大。比如将处理失败的请求放入一个专门的补偿队列,等待失败原因消除后进行补偿,重新处理。补偿最典型的使用场景是事务补偿。在一个分布式应用中,多个相关事务操作可能分布在不同的服务器上,如果某个服务器处理失败,那么整个事务就是不完整的。按照传统的事务处理思路,需要进行事务回滚,即将已经成功的操作也恢复到事务以前的状态,保证事务的一致性。传统的事务回滚主要依赖数据库的特性,当事务失败的时候,数据库执行自己的 undo 日志,就可以将同一个事务的多条数据记录恢复到事务之初的状态。但是分布式服务没有 undo 日志,所以需要开发专门的事务补偿代码,当分布式事务失效的时候,调用事务补偿服务,将事务状态恢复如初。
降级
在高并发场景下,如果系统的访问量超过了系统的承受能力,可以通过限流对系统进行保护。限流是指对进入系统的用户请求进行流量限制,如果访问量超过了系统的最大处理能力,就会丢弃一部分用户请求,保证整个系统可用。这样虽然有一部分用户的请求被丢弃,但大部分用户还是可以访问系统的,总比整个系统崩溃,所有的用户都不可用要好。
限流
降级是保护系统高可用的另一种手段。有一些系统功能是非核心的,但是也给系统产生了非常大的压力,比如电商系统中有确认收货这个功能,即便用户不确认收货,系统也会超时自动确认。但实际上确认收货是一个非常重的操作,因为它会对数据库产生很大的压力:它要进行更改订单状态,完成支付确认,并进行评价等一系列操作。如果在系统高并发的时候去完成这些操作,那么会对系统雪上加霜,使系统的处理能力更加恶化。解决办法就是在系统高并发的时候(例如淘宝双十一),将确认收货、评价这些非核心的功能关闭,也就是对系统进行降级,把宝贵的系统资源留下来,给正在购物的人,让他们去完成交易。
关于熔断降级和限流的详细内容可以参考"缓存问题“三件套“------- 雪崩,击穿与穿透"。
总结
到这里我们通过两篇文章讨论了在高并发系统中实现高可用的一些常用的方法论,主要涉及系统架构和系统间交互的一些优化手段,处理上文说的这些方案外,你还有其他的解决方案吗?