重试机制思考与实现

目录

背景:

场景:

问题:

失败场景以及造成结果:

方案:

思考:

重试引发问题:

思考:

实现重试:

切面方式

消息总线方式

模板方式

第三方组件

思考


背景:

在业务执行失败之后,重试一种常见的容错策略。保证数据最终的一致性。

场景:

第三方平台api调用(支付模块,邮件等等),第三方服务api调用(其他业务服务数据同步,步等),

问题:

失败场景以及造成结果:

  1. 网络抖动失败:面临问题->推送或者调用失败。
    1. 接收方没有成功,调用方失败
      1. 面临问题:推送失败,数据可能丢,业务数据不一致。
  2. 网络超时失败:面临问题->推送或者调用成功。
    1. 接收方处理成功,调用方失败
      1. 面临问题:调用方以为失败了,但接收方却接收到了,造成幂等问题。
  3. 业务处理失败:接收方正常,返回业务错误码,导致失败。
    1. 面临问题:失败就是失败了,需要人工干预。那么怎么通知开发人员,所以采用邮件,短信等等机制。

方案:

思考:

面临上面失败问题,怎么去保证提高推送成功的可能性,因此想到重试,所以重试是为了提高成功的可能性。

重试引发问题:

那么我们重试要

  1. 重试少次?
  2. 怎么实现重试策略?

因为一直重试可能会造成业务线程一直被重试占用,这样会导致服务的负载线程暴增直至服务宕机,因此需要限制重试次数。

那么如果是因为网络抖动,服务断线,我们一直重试,会造成重试浪费,造成资源浪费。所以怎么样去采用重试的时机。

思考:

那么既然有重试次数,如果我们重试也都失败了怎么办?不还是失败了嘛。

不也是可能会失败嘛。

所以我们知道重试只是提高成功的可能性。由此看,所有的方案都是提高成功的概率,如果重试都成功,服务挂了,全部断网,等等,一样会失败。所以我们实现都是近似值。到达一定程度,只能通过人为干预。

实现重试:

重试有哪些方案?怎么选择?

通过上面背景与问题分析,可以总结重试机制要素

  • 限制重试次数
  • 每次重试的时间间隔
  • 最终失败结果的报警或事物回滚
  • 在特定失败异常事件情况下选择重试

有了这些要素,就知道了我们的目标

我们的目标是实现一个优雅的重试机制,那么先来看下怎么样才算是优雅。

  • 无侵入:这个好理解,不改动当前的业务逻辑,对于需要重试的地方,可以很简单的实现
  • 可配置:包括重试次数,重试的间隔时间,是否使用异步方式等
  • 通用性:最好是无改动(或者很小改动)的支持绝大部分的场景,拿过来直接可用

所以我们猜测:

切面方式

这个思路比较清晰,在需要添加重试的方法上添加一个用于重试的自定义注解,然后在切面中实现重试的逻辑,主要的配置参数则根据注解中的选项来初始化

优点:

  • 真正的无侵入

缺点:

  • 某些方法无法被切面拦截的场景无法覆盖(如spring-aop无法切私有方法,final方法)
  • 直接使用aspecj则有些小复杂;如果用spring-aop,则只能切被spring容器管理的bean

消息总线方式

这个也比较容易理解,在需要重试的方法中,发送一个消息,并将业务逻辑作为回调方法传入;由一个订阅了重试消息的consumer来执行重试的业务逻辑

优点:

  • 重试机制不受任何限制,即在任何地方你都可以使用
  • 利用EventBus框架,可以非常容易把框架搭起来

缺点:

  • 业务侵入,需要在重试的业务处,主动发起一条重试消息
  • 调试理解复杂(消息总线方式的最大优点和缺点,就是过于灵活了,你可能都不知道什么地方处理这个消息,特别是新的童鞋来维护这段代码时)
  • 如果要获取返回结果,不太好处理, 上下文参数不好处理

模板方式

把这个单独捞出来,主要是某些时候我就一两个地方要用到重试,简单的实现下就好了,也没有必用用到上面这么重的方式;而且我希望可以针对代码快进行重试

优点:

  • 简单(依赖简单:引入一个类就可以了; 使用简单:实现抽象类,讲业务逻辑填充即可;)
  • 灵活(这个是真正的灵活了,你想怎么干都可以,完全由你控制)

缺点:

  • 强侵入
  • 代码臃肿

第三方组件

guava-retryingspring-retry 实际上是更好的选择,设计与实现都非常优雅,实际的项目中完全可以直接使用

框架中应用

  1. rocketMQ 消息可靠性
  2. spring cloud 注册中心

思考

既然我们有了实现的方式,那么思考,上面的问题是不是都解决了呢?

  1. 好像重试结束后还是失败没解决

我们可以采用死信队列DLQ。因为

重试就会导致后续的消费无法被消费,就会导致消息的堆积。所以他就把是失败的消息,给另外一个队列,然后去处理就行 了。

还有一种处理方式:

我们不希望消息,重试这么多,

那么我们会有一个消息重试记录表,

就是判断多少次,之后,我们持久化到数据库,

  1. 好像幂等问题没有解决

他会造成重复消费问题。

怎么解决呢?

第三方服务比较好解决:通过状态机幂等 或者访问标识,每一个请求携带一个请求。

第三方平台怎么解决?

当时支付请求发起成功,突然断网,发起方判断不了是否支付成功,此时如何处理?

状态判断

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

平凡之路无尽路

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值