为了账号安全,请及时绑定邮箱和手机立即绑定

【架构思考】如何根据业务场景选择合适的异常处理策略?

标签:
Java

大家好,我是姚半仙,慕课网《Java架构师成长直通车》课程架构师讲师团成员之一。今天我想和大家一起来探讨下异常处理策略的脱坑指南。

不做无用功

之所以我们需要选择合适的异常处理策略,因为还能再抢救一下,让应用从错误的状态恢复回来。但是对于无可救药的异常来说,我们就要果断抛弃,比如下面的两个例子:

  1. 参数异常 假如我们的Consumer接受的参数是数字类型的ID,结果Producer那边传来的是个String,导致反序列化都成问题
  2. 非法数据 比如我们需要接收一个商品ID然后发布上架,但是上游应用发来的商品ID压根不存在

在上面的情况下,即使重试到地老天荒也解决不了问题,重试纯粹是在做无用功。如果你的代码逻辑能明确获知这是一个不能靠重试解决的问题,我的建议是选择下面两个方法之一

  1. 转入其他队列 可以设置一个Error Queue用来存放这种异常消息,或者直接塞入死信队列
  2. 丢弃消息 装作什么事都没有发生,直接静默处理

即便我们选择了第一个处理方式,这类异常最终也得人工干预(我一般都直接丢弃,谁叫上游乱发数据)

Requeue的天坑

大家如果在项目里有下面这种配置,那要格外小心了

spring.cloud.stream.bindings.myconsumer.consumer.max-attempts=1

spring.cloud.stream.rabbit.bindings.myconsumer.consumer.requeue-rejected=true

这段配置将让Requeue功能进入狂暴模式,什么意思呢?就是在异常情况下,一个失败的消息会被重复提交到队列之中,直到被成功消费,或者等着Stream自己抛出AmqpRejectAndDontRequeueException异常才会停下来。

那我们想一下,假如碰上了前面说的那种参数异常的情况,重试解决不了问题。而这时又恰巧你配置了requeue-rejected=true并设置了max-attempts=1,那就陷入了无限循环之中,这对你的Consumer应用和外部消息组件都是一种负担。

重试+状态检查

我们在重试过程中经常会忽略做状态检查,我们来思考这样一个场景:

假如我在做商品批量上架和下架的任务,由于操作量特别大所以这里借助了Stream平滑流量,我先进行了下架任务,后面又触发了上架任务,如果没有异常中断的话,我的商品最终应该是处于上架的状态。

基于这个场景,我们来构造一个异常情况,比如说某一个商品的下架过程失败了,理所当然的就触发了重试操作。那这时候问题来了,在重试请求被处理之前,如果上架操作先被执行了,紧接着商品又在重试操作下被下架了。那么这个商品的最终状态是不是就变成了”下架“,这就和期望的状态发生了不一致。

所以我们在解决这类有”先后“顺序要求的操作时,要记得做一些状态检查,打个比方,我们可以向消息体中加入一个Version属性做版本控制,就像乐观锁机制一样,如果操作对象的当前版本高于消息中的版本,那么就不作处理。也可以在Consumer逻辑里面加入状态检查等,解决方案很多。

你在错上加错吗?

重试可以解决很多“临时问题”,就是一些因为网络或环境问题导致的短时间服务不可用,比如极短时间的网络阻塞。但是某些情况下重试可能会使问题雪上加霜。

同学们还记得Ribbon章节里我们讲到过,重试只能应用在以下两个场景中:

  1. 读取数据的服务,不会对数据进行任何修改
  2. 实现幂等性的写服务

对于写接口来说,如果要应用重试机制的话,就要在方法中实现资源锁定和检查,提交失败以后还要回滚和释放资源。切记不要对没有实现幂等性的写接口使用重试策略。

考虑到大型公司一般都会要求通过分布式事务或者最终一致性来保证幂等,但对于小规模的项目来说,如果没有实现幂等性的服务抛出了异常,建议直接推到死信队列或者另一个Queue中等待人工介入。

不妨考虑Fallback

在考虑使用重试、重新入队和死信队列以前,我们最好先评估一下,这个异常是否可以换个方式再抢救一把。我们不妨换Fallback来试试,这里的Fallhback就是我们前面Demo中通过@ServiceActivator注解实现的自定义的异常处理逻辑。我们再拿商品上下架的例子来说:

我们使用消息队列完成“下架商品”的任务,而正常的业务逻辑在执行下架操作时发现这个商品在数据库层面被锁了,这时候update语句就可能抛出异常,因为你无法预知该商品什么时候会被解除锁定,所以立即重试的意义也不大。这时候不妨尝试过一段时间在重试,我们可以利用Fallback + 延迟消息队列的形式,在Fallback里发送一个延迟消息,比如等待1分钟以后再次尝试下架商品的任务。

点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消