RabbitMQ:应用问题

目录

幂等性

幂等性的概念

MQ的幂等性

保证幂等性

消息积压

顺序性保障

如何保障顺序性


幂等性

幂等性的概念

在应用程序中,幂等性指对同一系统使用相同参数重复调用时,无论请求多少次,对系统的影响始终相同。 例如,支付接口若因网络重试被多次调用,最终应确保仅扣款一次

例如数据库的 select 操作,不同时间两次查询结果可能不同,但该操作仍符合幂等性。幂等性关注的是对资源的影响,而非返回结果。查询操作本身不会改变数据资源,结果差异通常因两次查询间其他操作修改了资源所致。

而 i++ 这类操作则不具备幂等性。若调用方未妥善控制逻辑,同一流程重复调用多次,将导致不同结果。

MQ的幂等性

MQ的幂等性是指,同一条消息被多次消费,对系统的影响是相同的,一般MQ的消息传输分为三个层级

  1. At most once:最多⼀次.消息可能会丢失,但绝不会重复传输.(可靠性比较低)
  2. At least once:最少⼀次.消息绝不会丢失,但可能会重复传输.(可靠性高)
  3. Exactly once:恰好⼀次.每条消息肯定会被传输⼀次且仅传输⼀次.(很难做到)

Rabbit支持最多一次和最少一次,最少一次的可靠性要高于最多一次,因为最多一次可能会因为一些原因导致消息丢失。

可能导致消息重传的场景

**生产者将消息发送给MQ时:**当有消息成功发送到MQ后,此时因为网络问题生产者宕机,导致MQ对生产者的应答失败,如果之后生产者意识到消息发送失败并且尝试重新发送,那么消费者就会收到两条相同的消息了并且ID一致。

**MQ向消费者投递消息:**消息已经都递给消费者并完成业务处理,在向MQ反馈应答时因为网络问题,MQ没有接收到。为了保证消息至少被消费一次,MQ会在网络恢复后再次尝试投递该消息,此时消费者就会收到两条相同的消息了,处理两个同样的消息可能就会造成一些问题比如连续扣两次款

保证幂等性

  • **唯一标识(幂等键):**客户端为每个请求生成全局唯一ID(如 UUID、业务主键),服务端校验该ID是否已处理,适用场景接口调用、消息消费等。
  • **数据库事务 + 乐观锁:**通过版本号或状态字段控制并发更新,确保多次更新等同于单次操作,适用场景数据库记录更新(如余额扣减、订单状态变更)。
  • 数据库唯一约束:利用数据库唯一索引防止重复数据写入,适用场景数据插入场景(如订单创建)。
  • **分布式锁:**通过锁机制保证同一时刻仅有一个请求执行关键操作,适用场景高并发下的资源抢夺(如秒杀)。
  • **消息去重:**消息队列生产者为每条消息生成唯一的消息 ID,消费者在处理消息前,先检查该消息 ID 是否已经处理过,如果已经处理过则丢弃该消息。

消息积压

消息积压就是因为生产者的生产速度,大于消费者的消费速度,导致大量消息没有被处理一直堆积在队列里。

消息积压产生的原因:
消息生产过快: 在高流量或高负载的情况下,生产者以极高的速率发送消息,超过了消费者的处理能力。
**消费者处理能力不足:**消费者处理消息的速度跟不上消息生产的速度,也会导致消息在队列中积压。可能原因有:

  1. 消费端业务逻辑复杂,耗时长
  2. 消费端代码性能低
  3. 系统资源限制,如CPU、内存、磁盘I/O等也会限制消费者处理消息的效率
  4. 异常处理不当。消费者在处理消息时出现异常,导致消息无法被正确处理和确认

网络问题: 因为网络延迟或不稳定,消费者无法及时接收或确认消息,最终导致消息积压
RabbitMQ服务器配置偏低

解决方案

遇到消息积压时,首先要分析消息积压造成的原因,根据原因来调整策略。主要从以下几个方面来解决:

  1. 提高消费者效率
    a. 增加消费者实例数量,比如新增机器
    b. 优化业务逻辑,比如使用多线程来处理业务
    c. 设置prefetchCount,当一个消费者阻塞时,消息转发到其他未阻塞的消费者
    d. 消息发生异常时,设置合适的重试策略,或者转入到死信队列

  2. 限制生产者速率,比如流量控制、限流算法等
    a. 流量控制:在消息生产者中实现流量控制逻辑,根据消费者处理能力动态调整发送速率
    b. 限流:使用限流工具,为消息发送速率设置一个上限
    c. 设置过期时间。如果消息过期未消费,可以配置死信队列,以避免消息丢失,并减少对主队列的压力

  3. 资源与配置优化,比如升级RabbitMQ服务器的硬件,调整RabbitMQ的配置参数等

上述策略也需要根据实际业务情况选择

顺序性保障

消息的顺序性就是指,消费者消费的数据和生产者发送的数据顺序一致。不过其实在很多场景下是不需要保证消息的顺序性的,除非是一些对顺序性要求严格的操作,比如银行转帐的顺序,或者同一用户短时间内进行大量的个人信息修改操作,这种就要求以最后一次修改为准。

RabbitMQ并不能保证绝对的顺序性,首先生产者发送消息的到达顺序都不能知晓(先发的消息可能后到达),那么也就无法保证顺序性了。

以下是会打破顺序性的一些场景

常见导致消息顺序性无法保证的场景如下:

  • **多个消费者:**当队列配置了多个消费者时,消息可能被不同消费者并行处理,导致处理顺序与发送顺序不一致。
  • **网络波动或异常:**消息传递过程中若出现网络波动或异常,可能使确认(ACK)丢失,消息被重新入队并再次被消费,从而打乱顺序。
  • **消息重试:**若消费者未及时发送确认,或确认在传输中丢失,MQ 会认为消息未成功消费而进行重试,重试时顺序可能被打乱。
  • **消息路由:**问题在复杂路由场景中,消息依据路由键被分发到不同队列,全局顺序无法得到保证。
  • **死信队列:**消息因消费端拒绝等原因进入死信队列后,再次消费时的顺序与生产者发送顺序可能不一致。

如何保障顺序性

全局性的顺序性很难保障,可以考虑在业务逻辑上来进行比如给消息加上序列号,所以一般只会要求局部顺序性,在单个队列内部保证消息的顺序。

首先需要明确业务场景中哪些消息是需要保证顺序的,对于需要顺序处理的消息,要确保消息队列和消费者能够按照特定的顺序进行处理。

  • **单个队列单个消费者:**最简单的办法是使用单个队列,并由单个消费者进行处理。同一个队列中的消息是先进先出的,这是 RabbitMQ 帮我们保证的
  • **分区消费:**单个消费者的吞吐太低,当需要多个消费者以提高处理速度时,可以使用分区消费。把消息按一定规则(如用户 ID 哈希)划分到多个分区,每个分区由一个消费者处理,从而保证每个分区内消息的顺序性。
  • **消息确认机制:**使用手动确认(manual ack),消费者处理完一条消息后显式发送确认,RabbitMQ 收到 ack 后才投递下一条,防止因异常或重试导致顺序错乱。
  • **业务逻辑控制:**若消息无法避免乱序,可在消息中携带序列号、时间戳或业务状态版本号,在消费端按这些信息排序或幂等处理,确保最终顺序一致
相关推荐
百***06012 小时前
五大消息模型介绍(RabbitMQ 详细注释版)
java·rabbitmq·java-rabbitmq
2501_941142933 小时前
Go语言高性能并发编程实践分享:从基础协程到分布式服务优化实战经验总结
rabbitmq
回家路上绕了弯3 小时前
接口 QPS 从 100 飙到 1000?从应急到根治的全流程优化方案
分布式·后端
象象翔3 小时前
服务异步通讯---rabbitmq的高级特性
分布式·rabbitmq
2501_941144424 小时前
Elasticsearch搜索与日志分析实践分享:高性能索引优化与实时检索经验
rabbitmq
2501_941404317 小时前
Go语言高性能WebSocket实时通信实战分享:微服务实时推送与并发优化经验
rabbitmq
2501_941823067 小时前
JavaScript Node.js高并发WebSocket服务设计与实时聊天平台落地经验分享:厦门社交应用实践
rabbitmq
2501_941823377 小时前
区块链与物联网:构建智能化、去中心化的未来生态
rabbitmq