Rabbitmq应用问题

幂等性

幂等性是数学和计算机上的某些运算性质,他们可以被多次应用,而不会改变初始应用的结果。比如-1进行绝对值之后变成1,后面不管怎样绝对值都是变成1。

比如我们通常会要求支付接口符合幂等性,多次调用同一个订单,同一个参数,对系统的影响是一样的(只能支付一次)。同一个订单,订单系统多次调用支付系统,支付系统只能进行一次扣款。

mq的幂等性指的是,同一条消息,多次消费,对系统的影响是相同的 ,市面上的mq基本都是只支持最多一次和最少一次。可靠性比较高的时候就使用最少一次,可靠性比较低的场景用最多一次。

最少一次的问题

  1. 当生产者发送消息给mq的时候,mq可能因为网络问题没有返回给生产者确认应答,生产者就会认为没有发送成功,导致发送过多,使得消费者处理多次。
  2. 也有可能mq发给消费者,消费者返回确认应答网络出现问题,也会导致mq向消费者发送多次。

解决方法(面试重点)

全局唯一id

给每条信息分配唯一的id,当消费者收到重复的消息,可以通过全局唯一id(比如uuid,或者自增id,业务id,时间戳+业务id)进行判定。消费者通过id进行判定,如果有已经消费过的,那就把消息进行抛弃。没有的话就把业务id存起来,比如数据库redis。

业务判定

比如判断数据库中是否有相关数据的存储,确保消息还没有被处理,然后才进行处理,比如看订单比如已经被取消了,如果已经被取消了,那不管怎么样都不进行处理。

顺序性保证

消费者消费消息的顺序和生产者生产消息的顺序相一致,比如生产者发送123,消费者消费123。如果消息没有丢失等等,才能保证消息顺序性。

同一时间对用户昵称进行修改,需要保证顺序性的保证,才能保证在多次修改的情况下能够最终修改成功。

打破rabbitmq的顺序性

多消费者

多个生产者发送消息顺序可以保证,但是mq接收消息的顺序不能保证,但是我们可以强制保证消费的顺序。多个消费者并行处理消息,相互之间并不相互影响。

网络波动

网络波动导致消息的ack出现问题 ,可能会重发,比如消费者1挂了,没有顺序消费消息。

死信队列

虽然排队排的早,但是因为某些原因没有成功发送给消费者,而是交给了死信队列。这时候后面的消息反而先消费,死信队列却没有消费

消息顺序性保证方法

局部顺序性保障

局部顺序性保证就是在一个队列(单个队列单个消费者)中要顺序处理,不能跳过,但是其他队列的处理相互之间不受影响,相互之间可以分别处理。

全局顺序性保障

全局性保证要保障每个队列(多个队列,多个消费者)都进行顺序性,比如要求是12345这样的顺序,不管这些消息分散在各个队列,都要按照这样的顺序进行消费。

单个队列,单个消费者。

分区消费,比如购买A商品,就可以通过一个订单一个订单号创建一个队列,这个订单的修改等都可以在一个队列中完成。

实际上rabbitmq没有实现分区算法,根据订单id进行hash算法,进行分散到多个算法,保证同样的订单id,进行算法分散之后,永远路由到某个队列。

消息积压(面试常考)

  • 生产者生产速度比较快,导致消费者没时间消费,消息堆积在队列中。
  • 消费者消费能力不足,消费者处理速度跟不上。
  • rabbitmq服务器配置不行。
  • 网络问题,导致队列中的消息没办法让消费者确认,就会导致某个消息没有收到确认一直堆积在队列口。

解决方法:

### RabbitMQ 的实际应用场景及示例代码 #### 实际应用场景 RabbitMQ 是一种基于 AMQP 协议的消息中间件,广泛应用分布式系统中的异步处理和解耦合需求。以下是其常见的实际应用场景: 1. **订单系统的异步通知** 在电商系统中,当用户完成下单操作后,可以将“发送短信”的任务交给 RabbitMQ 处理[^3]。这样可以让核心业务逻辑快速返回结果给用户,提升用户体验。 2. **日志收集与分析** 高并发系统会产生大量日志数据,通过 RabbitMQ 将这些日志消息传递到集中式的日志服务器上进行存储和分析[^4]。 3. **文件上传后的处理** 当用户上传大文件时,可以通过 RabbitMQ 将后续的任务(如压缩、转码等)放入队列中执行,从而减少前端等待时间[^5]。 4. **定时任务调度** 结合延迟队列功能,可以在指定的时间触发某些事件,比如优惠券到期提醒或者邮件推送[^1]。 5. **微服务之间的通信** 微服务体系架构下,不同服务之间可能需要相互通信但又不想直接调用接口,则可借助于 RabbitMQ 来实现松散耦合的消息交互方式[^2]。 --- #### 示例代码 下面提供了一个简单的 Spring Boot 和 RabbitMQ 集成的例子,展示如何向队列发送消息并消费它。 ##### 依赖配置 (pom.xml) ```xml <dependencies> <!-- Spring Boot Starter for RabbitMQ --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-amqp</artifactId> </dependency> <!-- Lombok for reducing boilerplate code --> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> </dependencies> ``` ##### 发送者类 (Sender.java) ```java import org.springframework.amqp.rabbit.core.RabbitTemplate; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @Component public class Sender { @Autowired private RabbitTemplate rabbitTemplate; public void send(String message, String queueName) { System.out.println("Sending message: " + message); this.rabbitTemplate.convertAndSend(queueName, message); // Send the message to specified queue. } } ``` ##### 接收者类 (Receiver.java) ```java import org.springframework.amqp.rabbit.annotation.RabbitListener; import org.springframework.stereotype.Component; @Component public class Receiver { @RabbitListener(queues = "#{queue.name}") // Dynamically resolve queue name from configuration. public void receiveMessage(String message) { System.out.println("Received message: " + message); } } ``` ##### 主程序启动类 (Application.java) ```java import org.springframework.amqp.rabbit.connection.ConnectionFactory; import org.springframework.amqp.rabbit.core.RabbitAdmin; import org.springframework.amqp.support.Queue; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.AnnotationConfigApplicationContext; @SpringBootApplication public class Application { public static void main(String[] args) throws InterruptedException { ApplicationContext context = new AnnotationConfigApplicationContext(Application.class); ConnectionFactory connectionFactory = context.getBean(ConnectionFactory.class); RabbitAdmin admin = new RabbitAdmin(connectionFactory); Queue queue = new Queue("testQueue", true); // Declare a durable queue named 'testQueue'. admin.declareQueue(queue); Sender sender = context.getBean(Sender.class); while(true){ Thread.sleep(1000L * 5); // Wait five seconds between each sending operation. sender.send("Hello RabbitMQ!", "testQueue"); } } } ``` --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值