架构误区系列 2:exactly once 的消息中间件不需要考虑消息重投
消息队列是大家在架构设计中比较常用的中间件。消息队列对消息投递的可靠性保证包括 at most once、at least once 和 exactly once。exactly once 的消息队列能保证消息支给 consumer 投递一次(中间件视角)。所以这类的消息队列,在消费方是不是就不需要考虑消息重复投递的幂等性处理?
at least once 的消息队列比较常见,这类消息中间件保证消息的可靠性,但是牺牲了消息重复投递。
exactly once 的消息队列保证消息只给 consumer 投递一次。这类的消息中间件需要和 consumer 之间的 ack 机制,中间件收到 consumer 确定的 ack 后,会明确消息投递成功,不再重复投递。
从上看的描述可以看出,这个 exactly once 只是站在消息中间件视角的。这个中间如果中间件和 consumer 之间通讯或者 consumer 自身出现异常,都可能导致 consumer 收到消息但是 ack 未达消息队列,消息队列再次投递同一个消息。所以在消费者角度,一定还是需要考虑到消息重复投递的情况。
上述还只是从消息层面的幂等性分析。对于业务系统来说,更重要的是在业务层面上的幂等控制。在这个层面上,引起重复投递的情况就更多了:
消息发送方的抖动、重启、通讯问题引起的重复投递消息。如果发送方没有 msgId 和 bizId 之间的唯一映射逻辑(原则上也不应该做如此过度设计),就会引起相同 bizId 用不同 msgId 投递到消息队列的情况,消息队列并没有机制确定是重复消息,会同样都投递到 consumer。
消息中间件某些 bug 引起的重复投递。只要是软件,就会有 bug,说 exactly once,就一定不会有一些容灾方面异常会导致重投递么?
消费方和中间件之间通讯问题、消费方故障、消费方重启等导致的重复消费消息或者无法给中间件明确的 ack。这个前面已经有分析。
基于上述的分析,对于消息中间件,不管是 at least once 还是 exactly once 的,在业务层面,我们都需要考虑到幂等控制的逻辑。有了这层逻辑,在消息层面,其实我们没有太多的价值去做消息层面的幂等控制的设计,或者没有理由去要求消息中间件来保证 exactly once。
版权声明: 本文为 InfoQ 作者【agnostic】的原创文章。
原文链接:【http://xie.infoq.cn/article/070179ecfa415827e90d3c031】。文章转载请联系作者。
评论