消息传输保障
消息可靠传输一般是业务系统接入消息中间件时首要考虑的问题,一般消息中间件的消息传输保障分为三个层级。
- At most once: 最多一次。消息可能会丢失 ,但绝不会重复传输 。
- At least once: 最少一次。消息绝不会丢失,但可能会重复传输。
- Exactly once: 恰好一次。每条消息肯定会被传输一次且仅传输一次。
RabbitMQ 支持其中的 "最多一次" 和 "最少一次"。其中 "最少一次" 投递实现需要考虑以下这个几个方面的内容:
- 消息生产者需要开启事务机制或者 publisher confirm 机制,以确保消息可以可靠地传输到 RabbitMQ 中。
- 消息生产者需要配合使用 mandatory 参数或者备份交换器来确保消息能够从交换器路由到队列中,进而能够保存下来而不会被丢弃。
- 消息和队列都需要进行持久化处理,以确保 RabbitMQ 服务器在遇到异常情况时不会造成消息丢失 。
- 消费者在消费消息的同时需要将 autoAck 设置为 false ,然后通过手动确认的方式去确认己经正确消费的消息,以避免在消费端引起不必要的消息丢失。
"最多一次" 的方式就无须考虑以上那些方面,生产者随意发送,消费者随意消费,不过这样很难确保消息不会丢失 。
"恰好一次" 是 RabbitMQ 目前无法保障的。考虑这样一种情况,消费者在消费完一条消息 之后向 RabbitMQ 发送确认 Basic.Ack 命令,此时由于网络断开或者其他原因造成 RabbitMQ 并没有收到这个确认命令,那么 RabbitMQ 不会将此条消息标记删除。在重新建立连接之后, 消费者还是会消费到这一条消息,这就造成了重复消费。再考虑一种情况,生产者在使用 publisher confirm 机制的时候,发送完一条消息等待 RabbitMQ 返回确认通知,此时网络断开, 生产者捕获到异常情况,为了确保消息可靠性选择重新发送,这样 RabbitMQ 中就有两条同样的消息,在消费的时候,消费者就会重复消费 。
那么 RabbitMQ 有没有去重的机制来保证 "恰好一次" 呢?答案是并没有,不仅是 RabbitMQ , 目前大多数主流的消息中间件都没有消息去重机制,也不保障 "恰好一次"。去重处理一般是在业务客户端实现,比如引入 GUID (Globally Unique Identifier) 的概念。针对 GUID ,如果从客户端的角度去重 ,那么需要引入集中式缓存,必然会增加依赖复杂度,另外缓存的大小也难以界定 。 建议在实际生产环境中,业务方根据自身的业务特性进行去重,比如业务消息本身具备 ' 幂等 ' 性,或者借助 Redis 等其他产品进行去重处理。