RabbitMQ---应用问题

(一)幂等性介绍

幂等性是本身是数学中的运算性质,他们可以被多次应用,但是不会改变初始应用的结果

1.应用程序的幂等性介绍

包括很多,有数据库幂等性,接口幂等性以及网络通信幂等性等

就比如数据库的select操作,这个操作就是符合幂等性的,虽然不同时间查询的结果会不同,但是幂等性指的是对资源的影响,而不是返回结果,查询数据本质上是不会对资源产生影响的,所以即使两次查询结果不同,那也是因为查询间有一些其他的操作对资源进行了修改

我们再来看接口的幂等性,就是说同一个接口多次调用,对系统的影响是相同的,就比如多次调用支付接口(同一个订单),对系统的影响也是一样的(也就是只生效一次)

2.MQ的幂等性

对于MQ而言,幂等性是指,同一条消息,被多次消费,对系统的影响也是相同的

一般我们消息中间件的消息传输保障分为三个级别:

1)At most once:最多⼀次. 消息可能会丢失,但绝不会重复传输

2)At least once:最少⼀次. 消息绝不会丢失,但可能会重复传输.

  1. Exactly once:恰好⼀次. 每条消息肯定会被传输⼀次且仅传输⼀次.

RabbitMQ只支持最多一次和最少一次,对于恰好一次,我们目前还做不到这么精准

在业务使用中,对于一些可靠性高的场景,建议使用最少一次,以防止我们消息的丢失,在一些不需要高可靠性的场景,我们可以使用最多一次,就比如我们日志的记录

那我们就来看一下什么情况下会导致同一条消息被多次消费

首先就是发送时消息重复:

当一条消息已经成功发送到交换机并成功到达队列完成持久化,此时出现了网络问题导致MQ没有给生产者发送ack,这样就会导致生产者重新发送一条消息到交换机,此时就会导致发送时消息重复(这两条内容是一样的)

还有消费时消息重复:

当消息已经从队列给消费者并且完成业务处理应该返回ack的时候,网络出现问题,导致ack丢失了,这样MQ就会认为消息没有被成功消费,此时就会触发重试机制,MQ重新给消费者传递消息

最少一次会有一个问题,消费端会手动重复的消息,就会对同一条消息进行多次处理,就有可能出现一些问题,如果此时我们消息不是幂等性的,比如扣款业务,就会出现多次扣款的情况

3.解决方案

MQ消费者幂等性的解决方案一般有以下几种:

1)全局唯一id

1.为每一条消息都分配一个标识符,可以是UUID也可以是自增ID但是一定要保证唯一性

2.消费者收到消息后,先去判断该id是否消费过如果消费过就放弃处理,如果没消费,消费者就开始消费消息,并且把ID保存到数据库中

这里我们可以使用redis原子性操作setnx来保证幂等性,把唯一ID作为key放到redis中,返回1就说明之前没有消费过,返回0就说明之前存在,就放弃处理

2)业务逻辑判断

在业务逻辑层面实现消息处理的幂等性,就比如我们先判断数据库中是否有相关数据记录,或者使用乐观锁机制避免已被其他事务更改的数据,或者检查相关业务的状态,如果是未处理,我们再进行处理

(二)顺序性保证

消息的顺序性是指消费者消费的消息和生产者发送消息的顺序一致

在很多业务场景下,消息的消费是不用保证顺序的,只需要执行就可以了,就比如我们订单的处理,先处理哪一个都可以,但是还有一些就不可以,比如对用户信息的修改,一个用户在很短时间内对同一个资料进行修改,就需要我们保证消息的顺序

我们RabbitMQ本质上是不能够保障顺序性的,在不考虑消息丢失,网络问题,并且只有一个消费者和一个生产者,点对点的情况下,是可以保障消息的顺序性,如果有多个生产者同时发送消息,我们就无法确定到达队列的顺序,就更无法确定到达消费者的顺序,也就无法保证顺序性

那除了这种还有什么情况会打破顺序性?

1)有多个消费者:当队列有多个消费者的时候,消息会被不同消费者处理,有的消费者处理的快有的处理的慢,所以我们消息处理的顺序性是无法保证的

2)网络波动或异常:在消息传递时如果ack丢失,就会使得消息重新入队,重新消费,造成顺序性问题

3)消息重试:这个在多个消费者会出问题。如果消费者处理消息未完全确认,那么就会触发重试机制,会影响消息处理的顺序性问题

4)消息路由问题:消息会根据routingkey被映射到不同的队列,从而无法保证全局的顺序性

5)死信队列:如果消息被拒绝放到死信队列,那么就会导致消息被消费的顺序打乱

顺序性保障方案

那如何来保障我们消息的顺序性?

我们分为局部顺序性保证和全局顺序性保证

局部顺序性通常指在一个队列内保证消息顺序,全局顺序性是指在全部队列内保证消息顺序

在实际应用中,全局顺序很难实现,相对来说局部顺序更加常见和容易实现

接下来说一下

消息的顺序保障的常见方法

1.单队列单消费者

最简单方法就是使用单个队列,队列由一个消费者进行处理

2.分区消费

单个消费者吞吐量太低,我们需要多个消费者并且还要保障顺序的时候,就可以使用分区消费,把一个队列分成多个队列,每个队列分配一个消费者,我们可以根据id进行哈希然后分成多个队列,这样我们只需要保障每个队列的顺序性即可

但是RabbitMQ本身不支持分区消费,所以需要业务逻辑实现,我们可以使用Spring-cloud-stream来实现

Partitioning with the RabbitMQ Binder :: Spring Cloud Stream

3.消息确认机制

消息确认就是消费者在处理完一条消息后,显示发送确认,这样RabbitMQ才会继续发送其他消息

4.业务逻辑控制

在一些情况下,即使消息的顺序不对,也可以通过我们设置序列号,然后在消费信息时进行判断来处理,保证顺序性

注:我们保证顺序性不是说上面一个就可以,要把上面的方法进行结合

RabbitMQ本⾝并不保证全局的严格顺序性,特别是在分布式系统中.在实际应⽤开发中根据具体的业 务需求,可能需要结合多种策略来实现所需要的顺序保证

(三)消息积压问题

消息积压是指在消息队列中,待处理的消息超过了消费者的处理能力,导致消息在队列中不断积压

产生消息积压有以下原因:

1.消息生产过快:在高流量情况下,生产者用高速率发送消息,超过了消费者的处理能力

2.消费者处理能力慢:消费者消费消息的速度低于生产者生产的速度,导致队列积压

这里可能有以下原因:

消费者的业务逻辑复杂,消费端代码性能低,系统资源限制,异常处理不当

3.网络问题:网络延迟或者不稳定,消费者无法立刻接收或者确认消息,导致消息积压

4.RabbitMQ服务配置低,消息积压会导致系统性能下降,影响用户体验,导致系统崩溃

解决方案:

1.提高消费者效率

增加消费者实例数量,比如新增机器

提高业务逻辑,使用多线程进行处理

设置消息分发,当一个消费者阻塞,把消息分发给其他消费者

消息发生异常,设置重试策略,或者转入死信队列

2.限制生产者速度

流量控制:在消息生产者设置流量控制逻辑

限流:使用限流工具,给消息发送速率设置上限

设置过期时间:消息到过期时间后,可以配置死信队列,等消费者空闲后,再进行消费

3.资源和配置优化:

升级RabbitMQ服务器的硬件,调整MQ参数

相关推荐
回家路上绕了弯14 小时前
深入解析Agent Subagent架构:原理、协同逻辑与实战落地指南
分布式·后端
用户83071968408217 小时前
Spring Boot 集成 RabbitMQ :8 个最佳实践,杜绝消息丢失与队列阻塞
spring boot·后端·rabbitmq
用户8307196840823 天前
RabbitMQ vs RocketMQ 事务大对决:一个在“裸奔”,一个在“开挂”?
后端·rabbitmq·rocketmq
初次攀爬者4 天前
RabbitMQ的消息模式和高级特性
后端·消息队列·rabbitmq
初次攀爬者6 天前
ZooKeeper 实现分布式锁的两种方式
分布式·后端·zookeeper
让我上个超影吧7 天前
消息队列——RabbitMQ(高级)
java·rabbitmq
塔中妖7 天前
Windows 安装 RabbitMQ 详细教程(含 Erlang 环境配置)
windows·rabbitmq·erlang
断手当码农7 天前
Redis 实现分布式锁的三种方式
数据库·redis·分布式
初次攀爬者7 天前
Redis分布式锁实现的三种方式-基于setnx,lua脚本和Redisson
redis·分布式·后端
业精于勤_荒于稀7 天前
物流订单系统99.99%可用性全链路容灾体系落地操作手册
分布式