RabbitMQ学习笔记

RabbitMQ 是一个基于 AMQP(高级消息队列协议,Advanced Message Queuing Protocol) 的开源消息中间件,由 Erlang 语言开发,具有高可靠性、灵活路由、跨平台等特点,广泛应用于分布式系统中解耦服务、异步通信和流量削峰场景。

1

核心概念与架构

  • 消息队列(Message Queue)
    消息的存储容器,用于暂存生产者发送的消息,供消费者按需获取。
    每个队列独立存储消息,支持持久化(消息存储到磁盘)以保证可靠性。
  • 生产者(Producer)
    发送消息的应用程序或服务,将消息发布到交换机(Exchange)。
  • 消费者(Consumer)
    接收并处理消息的应用程序或服务,通过订阅队列获取消息。
  • 交换机(Exchange)
    负责接收生产者发送的消息,并根据路由规则(Routing Key)将消息路由到一个或多个队列。交换机类型:
    • Direct:严格匹配 Routing Key,一对一精准路由(如日志系统按级别过滤
    • Fanout:广播模式,将消息路由到所有绑定的队列(如实时通知多个服务)
    • Topic:基于通配符匹配(如 user.* 或 order.#),灵活路由到多个队列(如订单系统按类型和状态分发)。

在java客户端中声明交换机和队列可以在config类中通过Bean来声明也可以基于@RabbitListener注解来声明

  • config类中声明
@Configuration
public class FanoutConfiguration {

    @Bean
    public FanoutExchange fanoutExchange() {
        return new FanoutExchange("hmall.fanout2");
    }

    @Bean
    public Queue fanoutQueue1() {
        return new Queue("fanout.queue1");
    }

    @Bean
    public Binding fanoutBinding1(Queue fanoutQueue1, FanoutExchange fanoutExchange) {
        return BindingBuilder.bind(fanoutQueue1).to(fanoutExchange);
    }
}

or

@Configuration
public class FanoutConfiguration {

    @Bean
    public DirectExchange directExchange() {
        return new FanoutExchange("hmall.direct2");
    }
    @Bean
    public Queue directQueue1() {
        return new Queue("direct.queue1");
    }
    @Bean
    public Binding fanoutBinding1(Queue fanoutQueue1, FanoutExchange fanoutExchange) {
        return BindingBuilder.bind(fanoutQueue1).to(fanoutExchange).with("red");
    }
}

基于bean来声明交换机和队列不能一键生成多个Routing key,只能一个一个敲

所以有了第二种方法

  • 基于@RabbitListener
@RabbitListener(bindings = @QueueBinding(
            value = @Queue(name = "work.queue",durable = "true"),
            exchange = @Exchange(name = "work.exchange", type = ExchangeTypes.FANOUT),
            key = {"red","blue"}
    ))
    public void listenWorkQueue1(String msg) {
        System.out.println("消费者1收到work.queue消息:" + msg);
    }

通过这种方式可以一次生成多个Routing key,很方便

高级特性

生产者确认机制

1

2

网络波动时的重试机制
在遇到网络波动时,可以通过重试和重新连接来避免因网络问题导致的消息发送失败。

RabbitMQ的生产者确认机制
如果消息发送失败并非由于网络波动造成,RabbitMQ提供了生产者确认机制。开启该机制后,生产者在发送消息到RabbitMQ时,系统会返回一个确认回执。

如果消息成功投递,返回ack(确认)回执。
如果消息投递失败,返回nack(拒绝)回执。
基于这些回执,生产者可以判断消息是否成功传送,并根据需要决定是否进行重发。
通过上述手段,可以有效保障生产者消息的可靠性。

注意事项
虽然以上方法能有效提升消息可靠性,但它们也会带来系统负担和资源开销。因此,在大多数场景下,我们不必开启生产者确认机制,除非对消息可靠性有较高要求。对于一般需求,关闭确认机制是足够的。

数据持久化

如果没有数据持久化会导致两个问题

  1. 在消费者消费过慢或者生产者生产过快导致MQ不得不去阻塞队列从而引发MQ阻塞。
  2. 一旦MQ宕机,内存中的消息都会丢失

23

在进行100w条数据读写时,如果未进行持久化,会在40w条左右时自动进行持久化,从而造成阻塞。(图中波谷所示)
4

而如果开启持久化则会在写入的同时对数据进行持久化,缺点是峰值下降了2/3但是数据传输更稳定,没有线程阻塞的情况发生。
5

Lazy Queue

6

如何设置队列为lazy queue,在3.12以后控制台创建的队列默认为lazy queue,如果通过代码创建队列的话则需要再构造器中加入.lazy()开启lazy模式

8

消费者确认机制

  • ACK 成功处理消息
  • nack 消息处理事变
  • reject 消息处理失败并拒绝该消息
    9

这里Spring AMQP给了几种处理回执的方案

在这里插入图片描述

那如果业务逻辑写的有毛病,或者其他原因导致一直nack一直重试怎么办呢?

在这里插入图片描述
设置最大重试次数,在本地重试几次之后如果还不行就会直接删除该条消息。

但是这样做也有点弊端,所以可以设置MessageRecoverer,多次失败后将消息投递到异常交换机,交由人工处理。

业务幂等性

在这里插入图片描述

对于业务幂等的消息,可以为消息设置一个唯一id,执行完毕后存储到数据库中,下次再有方法可以去数据库查看是否处理过,如果处理过就跳过这条。

当然也可以结合业务本身做调整,比如修改订单状态为已支付,可以在修改前看看是否为未支付,如果是,那么直接修改,如果不是,那么则跳过

这一块关于面试提问的回答技巧
14

延迟消息

对于秒杀类商品来说,如果抢到购买资格而未付款,以前这种情况系统会设置一个定时任务来清,但是并不是每个人的订单都是同一时刻下的,所以这会导致时效性很差。这里MQ提供了个新思路,延迟消息,在消息传递后的XX时刻后对消息进行判断。

15

基于死信交换机,可以实现消息的延迟发送。

根据这个原理,可以优化取消超时订单的代码

16

但是直接设置为30分钟时效性会很低,可以设置为10s10s15s…时间依次上升,大多数用户可以在前1m内完成支付,剩下的少部分用户会陆陆续续完成支付,这样mq的压力会减轻很多。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值