今天我们聊一聊 为什么系统需要重试?重试有何风险?x-retry 是什么?

772 阅读4分钟

为什么要重试?

随着微服务日益盛行,服务节点之间的调用也越来越频繁,经常由于网络抖动等系统原因导致服务之间调用失败,此时如果使用重试可以提高服务节点之间调用的成功率,提高系统的稳定性。

image.png

重试不仅提高系统的稳定性还可以提高系统的数据的一致性, 可能是服务与服务之间、服务与中间件之间都存在一致性要求。现有的分布式事务不仅系统复杂度较高,影响程序运行效率,因此往往是选择最终一致性,即通过重试补偿的形式,达到最终一致性

image.png

重试的风险

重试能够提高服务稳定性,但是一般情况下大家都不会轻易去重试,或者说不敢重试,主要是因为重试的流量不可控,且有放大故障,产生重试风暴的风险。由于没有统一的重试管理机制,服务各自为战,产生重试流量指数放大效应,导致链路上多层都被打挂,系统雪崩

image.png

重试还会存在链路放大的效应,结合下图说明一下

image.png

假设现在场景是 Backend A 调用 Backend B,Backend B 调用 DB Frontend,均设置重试次数为 3 。如果 Backend B 调用 DB Frontend,请求 3 次都失败了,这时 Backend B 会给 Backend A 返回失败。但是 Backend A 也有重试的逻辑,Backend A 重试 Backend B 三次,每一次 Backend B 都会请求 DB Frontend 3 次,这样算起来,DB Frontend 就会被请求了 9 次,实际是指数级扩大。假设正常访问量是 n,链路一共有 m 层,每层重试次数为 r,则最后一层受到的访问量最大,为 n * r ^ (m - 1) 。这种指数放大的效应很可怕,可能导致链路上多层都被打挂,整个系统雪崩。

重试的成本

重试能提高系统的稳定性、保障数据一致性,但是重试也有一定的成本, 在一些公司内部没有统一的服务治理平台,基本都是各个业务系统各自为战,导致重试方案各不相同,比较简单就是使用for循环来实现,基本不会考虑重试的放大效应,这样很不安全,公司内部出现过多次因为重试而导致的事故,且出事故的时候还需要修改代码上线才能关闭重试,导致事故恢复也不迅速。

另外也有一些业务使用开源的重试组件,这些组件通常会考虑对直接下游的保护,但不会考虑链路级别的重试放大,另外需要业务方修改调用代码才能使用,对业务代码入侵较多,而且也是静态配置,需要修改配置时都必须重新上线。

基于以上的背景,为了让业务系统能够灵活安全的使用重试,我们实现了一个重试治理组件即x-retry具有以下优点:

  1. 管控重试流量,预防重试风暴,及早发现和预警,并且提供流程管理手段
  2. 保证易用性: 业务接入成本小。避免依赖研发人员的技术水平,保障重试的稳定性
  3. 灵活性: 能够动态调整配置,启动/停止任务,以及终止运行中的重试数据
  4. 操作简单:一分钟上手,支持WEB页面对重试数据CRUD操作。
  5. 数据大盘: 实时管控系统重试数据。
  6. 多样化退避策略: Cron、固定间隔、等级触发、随机时间触发
  7. 容器化部署: 服务端支持docker容器部署
  8. 高性能调度平台: 支持服务端节点动态扩容和缩容
  9. 多样化重试类型: 支持ONLY_LOCAL、ONLY_REMOTE、LOCAL_REMOTE多种重试类型
  10. 重试数据管理: 可以做到重试数据不丢失、重试数据一键回放
  11. 支持多样化的告警方式: 邮箱、企业微信、钉钉

下次我们在聊一聊x-retry的诞生和后期的规划。