
幂等性保障
幂等性(Idempotency) 是计算机科学和网络通信中的一个重要概念,指的是某个操作无论被执行多少次,所产生的效果与执行一次的效果相同。
应用程序的幂等性:
在应用程序中,幂等性就是指对一个系统进行重复调用(相同的参数),不论请求多少次,这些请求对系统的影响都是相同的结果。
比如数据库的 select
操作,不同时间两次查询的结果可能不同,但是这个操作是符合幂等性的,幂等性指的是对资源的影响,而不是返回结果。
查询操作对数据资源本身不会产生影响,之所以结果不同,可能是因为两次查询之间有其他操作对资源进行了修改。
比如 i++
操作,就是非幂等性的,如果调用方法没有控制好逻辑,一次流程重复调用好几次,结果就会不同。
MQ 幂等性 :
对于 MQ 而言,幂等性就是指同一条消息,无论被消费者消费多少次,对系统的影响都是一样的。
一般消息中间件的消息传输保障分为三个层级:
- 最多一次(At most once):消息可能会丢失,但绝不会重发。
- 至少一次(At least once):消息绝不会丢失,但有可能会重复消费。
- 恰好一次(Exactly once):消息绝对不会丢失,也不会重复消费。
RabbitMQ 提供了 At most once
和 At least once
两种消息传输保障,但并不提供 Exactly once
保障,这是因为主流的消息队列中间件都没有提供这种保障。
在实际业务中,建议使用 最少一次
。最多一次
可能会导致因为网络问题、消费出现异常等等问题,导致消息丢失。
但是 最少一次
就会导致消息重发导致消息重复消费:
- 服务器发送给生产者的确认报文可能会因为网络等问题而丢失,导致生产者认为消息发送失败,然后重新发送消息给服务器。
- 消费者发送给服务器的确认报文也可能会因为网络等问题而丢失,导致服务器认为消息消费失败,然后发送给消息到消费者。
解决方案
全局唯一ID
- 为每条消息都分配一个标识符,比如
UUID
或者消息的唯一ID,总之保证其唯一性即可。 - 消费者收到该消息后,先判断该消息是否已经被消费过,如果消费过,则忽略该消息。
- 如果未消费过,则消费该消息,并将该消息的唯一 ID 记录到数据库或者缓存中,以便下次判断。(可以使用
Redis
的setnx
命令实现)
业务逻辑判断
在业务逻辑层面实现消息的幂等性。
顺序性保障
消息的顺序性是指消费者消费消息的顺序和生产者发送消息的顺序一致。
如果有多个生产者同时发送消息,是无法确定消息到达 RabbitMQ Broker 的前后顺序,也就无法保证消息的顺序性。
有以下几种情况可能会打破消息的顺序性:
- 多个消费者:但队列配置多个消费者时,消息可能会被不同的消费者并行处理,从而导致消息处理的顺序性无法保证。
- 网络波动或异常:由于网络等异常,导致消息被重发,从而导致消息的顺序性被打乱。
- 消息重试:如果消费者处理消息失败,则会将该消息重新放入队列,也就是消息重试,导致消息的顺序性被打乱。
- 消息路由问题:在复杂的路由场景中,消息可能会根据不同的路由键被分发到不同的队列,从而导致消息的顺序性被打乱。
- 死信队列:如果消费者处理消息失败,则会将该消息放入死信队列,导致消息的顺序性被打乱。
保障方案
顺序性保障分为两种:全局性顺序保障 和局部性顺序保障。
全局性顺序保障是指在多个队列或多个消费者之间保证消息的顺序。
局部性顺序保障通常指的是在单个队列内部保证消息的顺序。
在实际开放中,全局性保障的实现较为复杂,RabbitMQ
作为一个分布式消息队列,保障的是高吞吐量和高可用性,而不是严格的顺序性保障,如果业务场景确实需要严格的顺序性保障,建议在应用层代码逻辑里作额外的处理。
常见的顺序性保障方案
- 单队列单消费者 : 最简单的方法是使用单个队列,并由单个消费者进行处理。同一个队列中的消息是先进先出的,这是
RabbitMQ
来帮助我们保证的。 - 分区消费 :单个消费者的吞吐太低了,当需要多个消费者以提高处理速度时,可以使用分区消费。把一个队列分割成多个分区,每个分区由一个消费者进行处理,以此来保持每个分区内消息的顺序性。
RabbitMQ
本身并不支持分区消费,需要业务逻辑去实现,或者借助Spring Cloud Stream
来实现。- 详细参考:Spring Cloud Stream RabbitMQ 分区消费
消息积压问题
消息积压是指在消息队列中,待处理的消息数量超过了消费者的处理能力,导致消费者处理不过来,消息堆积在队列中,积压的消息越来越多,最终导致消息队列的阻塞。
- 从生产者的维度看:消息生产过快,在流量高峰期,生产者以极高的速率发送消息,导致消息积压。
- 从消费者的维度 看:消费者处理能力不足,消费者处理不过来,消息积压。
- 消费端业务逻辑复杂,耗时长。
- 消费端代码性能低下。
- 系统资源限制,如 CPU、内存、磁盘IO 等限制消费者处理消息的效率。
- 异常处理不当,消费者在处理消息时出现异常,导致消息无法被正确处理和确认。
- 网络问题
- RabbitMQ 服务器配置低下
解决方案
- 提高消费者的效率 :
- 增加消费者实例数量,比如新增机器。
- 优化业务逻辑和性能
- 设置
prefetchCount
参数,限制每个消费者所能接收的消息数量,从而提高消费者的处理能力。 - 消息发生异常时,设置合理的重试策略,或者转入死信队列。
- 限制生产者的发送速度 :
- 流量控制
- 限流
- 设置过期时间
- 资源与配置优化 :
- 升级服务器硬件配置,提高 CPU、内存、磁盘IO 等资源的利用率。
- 优化 RabbitMQ 服务器配置,设置合理的配置。