前言
为什么引入消息队列?引入 MQ 给我们解决了一些问题,但同时又引入了一些复杂的问题,这些问题是大型项目中必须解决的重点,更重要的是,面试也经常问。实际上消息队列可以说是没法百分之百保证可靠性的!RabbitMQ 提供的相关机制也只是在于缩小消息丢失的概率,或者说提供了消息丢失后的我们可以记录日志的功能。 在解决这些问题时有必要明白一点,其实小公司业务量不大,并发量不高的情况下这些问题是几乎不会发生的......即使偶尔出现,开发人员手动修复数据处理就好。所以可结合公司实际业务场景看有没有必要解决这些问题
消息可靠性
以创建订单为例,可能会出现这样的业务场景
- MQ 挂了,消息没发出去。创建订单后面几个优惠券、积分的下游系统全都没有执行业务结算怎么办?
- MQ 是高可用的,消息发出去了,但是优惠券结算业务报错了怎么办?因为这个时候是异步的,也不好去回滚
- 消息正常发出去,消费者也接收到了,商户系统、优惠券系统都正常执行完了,积分业务报错了导致积分没结算,那这个订单的数据就不一致了
要解决上述问题,就是要保证消息一定要可靠地被消费,那么我们可以来分析下消息有哪些步骤会出问题。RabbitMQ 发送的消息是这样的:
消息被生产者发到指定的交换机根据路由规则路由到绑定的队列,然后推送给消费者。在这个过程中有可能会发生消息出问题的场景:
- 生产者消息没到交换机,相当于生产者弄丢消息
- 交换机没有把消息路由到队列,相当于生产者弄丢消息
- RabbitMQ 宕机导致队列、队列中的消息丢失,相当于 RabbitMQ 弄丢消息
- 消费者消费出现异常,业务没执行,相当于消费者弄丢消息
生产者弄丢消息
RabbitMQ 提供了确认和回退机制,有一个异步监听机制,每次发送消息,如果成功/未成功发送到交换机都可以触发一个监听,从交换机路由到队列失败也会有一个监听。只需要开启这两个监听机制即可,以 SpringBoot 整合 RabbitMQ 为例
引入依赖 starter
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>