系统健壮设计 – 补偿机制

背景

分布式系统中,一次跨机器的请求通信可能会通过很多的网络设备,可能包含负载均衡、DNS、交换机、路由器、网卡等,这些网络设备都不是一直稳定的,在数据传输的过程中只要一个有问题,就会造成这次通信失败的产生。一次完整的业务流程通常会有多次跨机器的通信构成的,产生问题的概率也会随之增加。

为了保障系统运行稳定性,我们制定了很多SLO的目标,我们还需要很多的手段和方法去达到并实现这个系统目标。做到系统对外的高可用,对内的覆盖错误或者消化掉异常,这不仅仅在服务Pod运维的维度,还包含业务系统逻辑正常的维度。

补偿机制

所谓补偿机制,即通过内部机制将异常产生的系统间不一致问题或者异常进行消除。

补偿机制主要有:

  • 重试机制,从结果来看重试则是正向操作,有处理成功的机会。
  • 事务补偿,事务补偿是逆向操作,结果为恢复操作前的状态,从而到达数据一致性。

两者适用的场景不同,重试更偏向于处理底层网络调用超时、网络中断、抖动等场景,设计优良的重试机制可以更大概率保证系统响应正常。

事务补偿则更贴近实际的业务,保证业务场景的数据一致性,比如在转账场景中扣减余额,再进行转账操作,若转账失败则进行余额恢复。下单商品库存扣减等场景。

重试

通常来说在使用重试机制时,要确定操作的影响(适用:网络抖动、网络中断、网络超时类似),在重新尝试后操作有一些成功的可能性时,才能重试操作

如果操作是无效的操作,例如数据库所更新的项不存在,或者请求的服务不支持接口幂等性,则重新尝试没有任意意义。

所以,我们需要一个重试的机制。我们需要了解的是,**” 重试 ” 的语义是我们认为这个故障是暂时的,而不是永久的,我们就可以去重试**。

案例:系统健壮设计 - 补偿机制

重试机制的设计重点:

  1. 要确定什么样的错误下需要重试;
  2. 重试的时间和重试的次数(不同的情况下要有不同的设计);
  3. 重试还需要考虑被调用方是否有幂等的设计(如果没有那么重试是不安全的);

事务补偿

事务补偿即在事务链中的任何一个正向事务操作,都必须存在一个完全符合回滚规则的可逆事务。

如果是一个完整的事务链,则必须事务链中的每一个业务服务或操作都有对应的可逆服务。

案例:

系统健壮设计 - 补偿机制

优点:能实现数据一致性。

不足:事务补偿的方式需要编写大量的代码来处理,以保证事务的完整性,还需要通用的事务管理器去实现事务链和事务上下文的管理。对于事务链上任何一个服务正向和逆向操作,由事务管理器管理事务补偿和回滚。

事务补偿设计的重点:

  1. 需要把整个流程所涉及的服务方支持幂等性,并且上游有重试机制;
  2. 需要提供反向操作接口,但不一定非得严格反向操作;
  3. 我们需要维护和监控整个事务链的过程和状态;

实际上我们在系统设计时,需要确认我们要的是实时一致性还是最终一致性,如果是最终一致性那么BASE策略中的最终一致性是比较好的方案。

BASE 策略

BASE 策略系统是允许或是容忍系统出现暂时性的问题的,这样我们的系统就能更有弹性。

因为在分布式系统故障是不可避免的,我们能做的就是把故障处理当任务或者功能写入代码中,进行容错设计。

为了提高系统性能,出现了 ACID 的一个变种 BASE。

  • Basic Availability:基本可用。这意味着,系统可以出现暂时不可用的状态,而后面会快速恢复。
  • Soft-state:软状态。它是我们前面的“有状态”和“无状态”的服务的一种中间状态。也就是说,为了提高性能,我们可以让服务暂时保存一些状态或数据,这些状态和数据不是强一致性的。
  • Eventual Consistency:最终一致性,系统在一个短暂的时间段内是不一致的,但最终整个系统看到的数据是一致的。

BASE 系统是允许或是容忍系统出现暂时性的问题的,这样我们的系统就能更有弹性。因为在分布式系统故障是不可避免的,我们能做的就是把故障处理当任务或者功能写入代码中,进行容错设计。

定时数据补偿

系统健壮设计 - 补偿机制
  1. 设置广告审核状态为”创建中”;
  2. 调用RPC创建广告引擎广告索引;
  3. 获得slice_id更新对应的广告进行关联;
  4. 定时任务扫描”创建中“的广告进行再次创建索引,直至成功,超过一定次数进行人工干预;

基于消息的最终一致性

系统通过分布式消息中间件进行解耦设计

系统健壮设计 - 补偿机制
  1. 设置广告审核状态为”创建中”;
  2. 写入本地消息处理状态,1和2为本地事务;
  3. 发送消息至消息中间件;
  4. 消费者消费消息,广告引擎再根据消息进行索引创建;
  5. 处理结果通知消息队列;
  6. 投放平台根据消息更新投放计划ID和投放状态;


结语

1.重试的场景并不是所有的失败场景都适合重试,我们在工程中可以用 Java 的 Annotation 来实现(Spring Retry 或 Guaua Retryer)。

2.BASE 的一致性 相对 ACID 较弱,但有很好的可伸缩性还可以异步批量处理,大多数分布式事务场景都适合适合 BASE。

3.要实现 BASE 事务,需要实现补偿逻辑,因为事务可能失败,此时需要协调各方进行撤销。


附录

  • https://my.oschina.net/u/3552299/blog/5117092

  • https://www.baiyp.ren/JAVA%E4%B8%AD%E7%9A%84%E5%88%86%E5%B8%83%E5%BC%8F%E4%BA%8B%E5%8A%A105.html

  • http://jverson.com/thinking-in-java/tools/guava-retryer.html

  • https://segmentfault.com/a/1190000022962709

  • https://juejin.cn/post/6844903844829200391


原文始发于微信公众号(程序猿阿南):系统健壮设计 – 补偿机制

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/22263.html

(0)
小半的头像小半

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!