RabbitMQ - 笔记

1. MQ的基本概念

1.1 MQ概述

MQ (Message Queue) 消息队列,是在消息的传输过程中保存消息的容器。多用于分布式系统之间进行通信。

在这里插入图片描述

1.2 MQ的优势和劣势

优势:

  • 应用解耦
  • 异步提速
  • 削峰填谷

劣势:

  • 系统可用性降低
  • 系统复杂度提高
  • 一致性问题

1.3 MQ的优势

  1. 应用解耦

在这里插入图片描述
直接调用,如果新增子系统需要修改主系统的代码。耦合度高、不利于扩展。

在这里插入图片描述
通过MQ间接调用,系统间解耦,提高容错性和扩展维护性。

  1. 异步提速

在这里插入图片描述
在这里插入图片描述
异步调用提高了用户的体验,提高了系统的吞吐量(单位时间内处理请求的数目)。

  1. 削峰填谷
    在这里插入图片描述
    在这里插入图片描述

在这里插入图片描述
使用MQ后,高峰期来的请求会积压在MQ中,订单系统的请求高峰就被削掉了。这就是削峰
在高峰期过后的一段时间内,订单系统处理请求仍会维持在1000,直到积压的消息被消费完。这就是添谷
使用MQ能提高系统的稳定性。

1.4 MQ的劣势

在这里插入图片描述

  • 系统的可用性降低
    系统引入的外部依赖越多,系统稳定性越差。一旦MQ宕机,就会对业务造成影响。如何保证MQ的高可用?
  • 系统的复杂度提高
    MQ的加入大大增加了系统的复杂度,以前系统间是同步的远程调用,现在通过MQ进行异步调用。如何保证消息没有被重复消费?怎么处理消息丢失情况?怎么保证消息传递的顺序性?
  • 一致性问题
    A系统处理完业务,通过MQ交给B、C、D三个子系统。如果B、C成功完成,D执行失败。怎么保证数据处理的一致性?

1.5 MQ的使用条件

  1. 生产者不需要从消费者处获取反馈,直接调用的接口返回值应该为空,才可以异步调用。比如 A系统直接调用B系统,如果A系统不需要B系统返回信息那么就可以异步调用,使用MQ.
  2. 容许短暂的不一致性。
  3. 使用了MQ的效果大于管理MQ的成本。

1.6 常见的MQ产品

在这里插入图片描述

追求可用性:Kafka、 RocketMQ 、RabbitMQ

追求可靠性:RabbitMQ、RocketMQ

追求吞吐能力:RocketMQ、Kafka

追求消息低延迟:RabbitMQ、Kafka

2. RabbitMQ简介

2.1 AMQP协议

AMQP协议: Advanced Message Queuing Protocol 高级消息队列协议。是应用层的网络协议,为面向消息的中间件设计。基于此协议的 客户端与消息中间件可传递消息,不受客户端/中间件不同产品,不同开发语言等条件的限制。类比于HTTP。
在这里插入图片描述

2.2 RabbitMQ架构

在这里插入图片描述

  • Broker: 接受和分发消息的应用,RabbitMQ Server 就是Message Broker
  • Virtual Host: 处于多租户和安全因素设计的,把AMQP的基本组件划分到一个虚拟的分组中。当多用户使用同一个RabbitMQ Server提供的服务时,每个用户在自己的vhost中创建exchange/queue等。
  • Connection: publisher、Consumer和Broker之间的TCP连接。
  • Channel: 如果每次访问RabbitMQ Server都建立一个Connection,效率低开销大。Channel是在Connection内部建立的逻辑连接,多线程每个线程都创建单独的Channel进行通信,有channel id用于区分。Channel作为轻量级的Connection极大减少了操作系统建立TCP Connection的开销。
  • Exchange: 根据分发规则,匹配查询查询表中的routing key,分发消息到queue中。常用类型有: direct、topic、fanout。
  • Queue: 消息存放的地方,等待consumer取走。
  • Binding: exchange和queue之间的虚拟连接,binding中可以包含routing key。Binding信息被保存到exchange中的查询表中,用于message的分发依据。

2.3 RabbitMQ的工作模式

工作模式: 生产、消费 消息的一种工作方式。

官网工作模式https://www.rabbitmq.com/getstarted.html

  1. BasicQueue 基本消息队列
    在这里插入图片描述
  2. WorkQueue 工作消息队列
    在这里插入图片描述
  3. Fanout Exchange 广播
    在这里插入图片描述
  4. Direct Exchange 路由
    在这里插入图片描述
  5. Topic Exchange 主题
    在这里插入图片描述

2.4 JMS

  • JMS即Java 消息服务(JavaMessage Service) 应用程序接口,是一个Java平台中关于面向消息中间件的API.
  • JMS是JavaEE规范中的一种,类比JDBC。
  • 很多消息中间件都实现了JMS规范,例如ActiveMQ。 RabbitMQ没有实现,但开源社区有。

3. RabbitMQ 安装

使用Docker 安装

  1. 在线拉取或本地加载镜像
docker pull rabbitmq:3.8-management
  1. 创建RabbitMQ容器
docker run \
 -e RABBITMQ_DEFAULT_USER=root \   # 设置 用户名
 -e RABBITMQ_DEFAULT_PASS=root \   # 设置 密码
 -v mq-plugins:/plugins \          # 可能用到插件,挂载插件目录
 --name mq \                       # 容器名
 --hostname mq \                   # 主机名
 -p 15672:15672 \                  # rabbitmq的控制台网页端口
 -p 5672:5672 \                    # 消息传输的端口
 -d \                              # 后台运行
 rabbitmq:3.8-management           # 使用的镜像版本

4. RabbiMQ快速入门

4.1 “Hello World!” 模式

在这里插入图片描述

  • 生产者生产消息到队列,消费者从队列获取消息消费。
  • 使用默认的交换机。

步骤:

  1. 创建工程 (生产者、消费者)
    在这里插入图片描述
  2. 分别添加依赖
        <!--RabbitMQ-->
        <dependency>
            <groupId>com.rabbitmq</groupId>
            <artifactId>amqp-client</artifactId>
        </dependency>
  1. 生产者发送消息代码
		//RabbitMQ
        //创建连接工厂
        ConnectionFactory connectionFactory = new ConnectionFactory();
        //设置参数
        connectionFactory.setHost("192.168.205.131"); //RabbitMQ所在的服务器地址
        connectionFactory.setPort(5672); //RabbitMQ 通信端口
        connectionFactory.setVirtualHost("order"); //虚拟主机名,隔离多租户
        connectionFactory.setUsername("root");
        connectionFactory.setPassword("root");
        //创建连接
        Connection connection = connectionFactory.newConnection();
        //创建channel
        Channel channel = connection.createChannel();
        //创建queue,简单模式使用默认的交换机
        channel.queueDeclare("queue", true, false, false, null);
        //发送消息
        String body = "hello RabbitMQ";
        channel.basicPublish("", "queue", null, body.getBytes());
        //关闭资源
        channel.close();
        connection.close();
  1. 消费者消费消息代码
		//创建工厂
        ConnectionFactory connectionFactory = new ConnectionFactory();
        //设置参数
        connectionFactory.setHost("192.168.205.131");
        connectionFactory.setPort(5672);
        connectionFactory.setVirtualHost("order");
        connectionFactory.setUsername("root");
        connectionFactory.setPassword("root");
        //创建连接
        Connection connection = connectionFactory.newConnection();
        //创建channel
        Channel channel = connection.createChannel();
        
        //获取消息
        Consumer consumer = new DefaultConsumer(channel) {
    
    
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
    
    
                System.out.println(consumerTag);
                System.out.println(envelope.getExchange());
                System.out.println(envelope.getRoutingKey());
                System.out.println(envelope.getDeliveryTag());
                System.out.println(properties);
                System.out.println(new String(body));
            }
        };
        //从queue队列获取消息
        channel.basicConsume("queue", consumer);

4.2 Work queues 模式

在这里插入图片描述

  • 在一个队列中有多个消费者,消费者之间是竞争的关系。
  • Work Queue对于任务过重或任务较多的情况下使用可以提高处理速度。

生产者代码

        //RabbitMQ
        //创建工厂
        ConnectionFactory connectionFactory = new ConnectionFactory();
        //设置参数
        connectionFactory.setHost("192.168.205.131");
        connectionFactory.setPort(5672);
        connectionFactory.setVirtualHost("order");
        connectionFactory.setUsername("itcast");
        connectionFactory.setPassword("123456");
        //创建连接
        Connection connection = connectionFactory.newConnection();
        //创建channel
        Channel channel = connection.createChannel();
        //创建queue
        channel.queueDeclare("work queue", true, false, false, null);
        //发送多条消息
        for (int i = 0; i < 10; i++) {
    
    
            String body = i + " hello RabbitMQ";
            channel.basicPublish("", "work queue", null, body.getBytes());
        }
        //关闭资源
        channel.close();
        connection.close();

消费者1代码

        //创建工厂
        ConnectionFactory connectionFactory = new ConnectionFactory();
        //设置参数
        connectionFactory.setHost("192.168.205.131");
        connectionFactory.setPort(5672);
        connectionFactory.setVirtualHost("order");
        connectionFactory.setUsername("itcast");
        connectionFactory.setPassword("123456");
        //创建连接
        Connection connection = connectionFactory.newConnection();
        //创建channel
        Channel channel = connection.createChannel();
        //获取消息
        Consumer consumer = new DefaultConsumer(channel) {
    
    
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
    
    
                System.out.println(new String(body));
            }
        };
        channel.basicConsume("work queue", consumer);

消费者2代码
和消费者1代码一样

演示情况
在这里插入图片描述

4.3 Publish/Subscribe 模式

在这里插入图片描述

  • 交换机类型:FANOUT
  • 交换机将消息分发给所有绑定的队列。
  • 多个消费者从各自的队列获取消息。
    生产者代码
        //RabbitMQ
        //创建工厂
        ConnectionFactory connectionFactory = new ConnectionFactory();
        //设置参数
        connectionFactory.setHost("192.168.205.131");
        connectionFactory.setPort(5672);
        connectionFactory.setVirtualHost("order");
        connectionFactory.setUsername("itcast");
        connectionFactory.setPassword("123456");
        //创建连接
        Connection connection = connectionFactory.newConnection();
        //创建channel
        Channel channel = connection.createChannel();
        //创建交换机
        channel.exchangeDeclare("fanout exchange", BuiltinExchangeType.FANOUT, true, false, false, null);
        //创建两个queue
        channel.queueDeclare("fanout queue1", true, false, false, null);
        channel.queueDeclare("fanout queue2", true, false, false, null);
        //绑定交换机和队列,广播路由规则为""
        channel.queueBind("fanout queue1","fanout exchange","");
        channel.queueBind("fanout queue2","fanout exchange","");
        //发送消息
        String body = "hello RabbitMQ";
        channel.basicPublish("fanout exchange", "", null, body.getBytes());
        //关闭资源
        channel.close();
        connection.close();

消费者代码
和之前没有区别,各个消费者从各自的队列获取消息就行了。

        //创建工厂
        ConnectionFactory connectionFactory = new ConnectionFactory();
        //设置参数
        connectionFactory.setHost("192.168.205.131");
        connectionFactory.setPort(5672);
        connectionFactory.setVirtualHost("order");
        connectionFactory.setUsername("itcast");
        connectionFactory.setPassword("123456");
        //创建连接
        Connection connection = connectionFactory.newConnection();
        //创建channel
        Channel channel = connection.createChannel();
        //获取消息
        Consumer consumer = new DefaultConsumer(channel) {
    
    
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
    
    
                System.out.println(new String(body));
            }
        };
        channel.basicConsume("fanout queue1", consumer);

4.4 Routing 模式

在这里插入图片描述

  • 交换机类型: DIRECT
  • 交换机在绑定队列时,需要指定 RoutingKey
  • 消息的发送方在向交换机发送消息时也需要指定消息的Routing Key.
  • Exchange在分发消息的时候,后根据Routing Key 分发给对应的队列。

生产者代码

在这里插入图片描述

4.5 Topics 模式

在这里插入图片描述

  • 交换机类型:Topic
  • RoutingKey 使用通配符。 * 一个单词,# 多个单词

生产者代码
在这里插入图片描述

5. Spring 整合RabbitMQ

引入依赖坐标

            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-context</artifactId>
                <version>5.3.19</version>
            </dependency>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-test</artifactId>
                <version>5.3.19</version>
            </dependency>
            <dependency>
                <groupId>org.springframework.amqp</groupId>
                <artifactId>spring-rabbit</artifactId>
                <version>2.2.15.RELEASE</version>
            </dependency>
            <dependency>
                <groupId>junit</groupId>
                <artifactId>junit</artifactId>
                <version>4.13</version>
                <scope>test</scope>
            </dependency>

生产者 Beans.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:rabbit="http://www.springframework.org/schema/rabbit"
       xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
                           http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd
                           http://www.springframework.org/schema/rabbit https://www.springframework.org/schema/rabbit/spring-rabbit.xsd">

    <!--加载配置文件-->
    <context:property-placeholder location="classpath:rabbitmq.properties"/>

    <!--定义ConnectionFactory Bean-->
    <rabbit:connection-factory id="connectionFactory"
                               host="${rabbitmq.host}"
                               port="${rabbitmq.port}"
                               username="${rabbitmq.username}"
                               password="${rabbitmq.password}"
                               virtual-host="${rabbitmq.virtual-host}"/>

    <!--创建管理员,管理交换机、队列、绑定。-->
    <rabbit:admin connection-factory="connectionFactory"/>

    <!--定义一些队列,如果下面没有和交换机绑定则使用默认交换机-->
    <rabbit:queue id="queue0" name="spring_queue_0" auto-declare="true"/>
    <rabbit:queue id="queue1" name="spring_queue_1" auto-declare="true"/>
    <rabbit:queue id="queue2" name="spring_fanout_queue_1" auto-declare="true"/>
    <rabbit:queue id="queue3" name="spring_fanout_queue_2" auto-declare="true"/>
    <rabbit:queue id="queue4" name="spring_direct_queue_1" auto-declare="true"/>
    <rabbit:queue id="queue5" name="spring_direct_queue_2" auto-declare="true"/>
    <rabbit:queue id="queue6" name="spring_topic_queue_1" auto-declare="true"/>
    <rabbit:queue id="queue7" name="spring_topic_queue_2" auto-declare="true"/>

    <!--定义fanout交换机并绑定队列-->
    <rabbit:fanout-exchange id="fanoutExchange" name="spring_fanout_exchange" auto-declare="true">
        <rabbit:bindings>
            <rabbit:binding queue="queue2"/>
            <rabbit:binding queue="queue3"/>
        </rabbit:bindings>
    </rabbit:fanout-exchange>

    <!--定义direct交换机并绑定队列-->
    <rabbit:direct-exchange id="directExchange" name="spring_direct_exchange">
        <rabbit:bindings>
            <rabbit:binding key="aaa" queue="queue4"/>
            <rabbit:binding key="bbb" queue="queue5"/>
        </rabbit:bindings>
    </rabbit:direct-exchange>

    <!--定义Topic交换机并绑定队列-->
    <rabbit:topic-exchange id="topicExchange" name="spring_topic_exchange" auto-declare="true">
        <rabbit:bindings>
            <rabbit:binding pattern="#.info" queue="queue6"/>
            <rabbit:binding pattern="#.error" queue="queue7"/>
        </rabbit:bindings>
    </rabbit:topic-exchange>

    <!---->
    <rabbit:template id="rabbitTemplate" connection-factory="connectionFactory"/>

</beans>

生产者测试代码

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

/**
 * @author : ZHX
 * @date : 2022/6/5 17:40
 * @description:
 */
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:beans.xml")
public class ProducerTest {
    
    

    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Test
    public void testHelloWorld() {
    
    
        //简单模式工作模式 发送消息,使用默认交换机、routingkey使用queue名字,
        rabbitTemplate.convertAndSend("spring_queue_0", "hello world");
        //工作模式
        rabbitTemplate.convertAndSend("spring_queue_0", "hello world");
        rabbitTemplate.convertAndSend("spring_queue_1", "hello world");

        //发布订阅模式
        //fanout交换机
        rabbitTemplate.convertAndSend("spring_fanout_exchange", "", "hello hacker");
        //Direct交换机
        rabbitTemplate.convertAndSend("spring_direct_exchange", "aaa", "hello ");
        rabbitTemplate.convertAndSend("spring_direct_exchange", "aaa", "hello ");
        rabbitTemplate.convertAndSend("spring_direct_exchange", "bbb", "hello ");
        //topic交换机
        rabbitTemplate.convertAndSend("spring_topic_exchange", "a.info", "hello ");
        rabbitTemplate.convertAndSend("spring_topic_exchange", "b.info", "hello ");
        rabbitTemplate.convertAndSend("spring_topic_exchange", "a.error", "hello ");
    }
}

消费者 beans.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:rabbit="http://www.springframework.org/schema/rabbit"
       xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
                           http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd
                           http://www.springframework.org/schema/rabbit https://www.springframework.org/schema/rabbit/spring-rabbit.xsd">

    <!--加载配置文件-->
    <context:property-placeholder location="classpath:rabbitmq.properties"/>

    <!--定义ConnectionFactory Bean-->
    <rabbit:connection-factory id="connectionFactory"
                               host="${rabbitmq.host}"
                               port="${rabbitmq.port}"
                               username="${rabbitmq.username}"
                               password="${rabbitmq.password}"
                               virtual-host="${rabbitmq.virtual-host}"/>

    <bean id="queue0Listener" class="rabbitmq.listener.queue0Listener"/>
    <bean id="queue1Listener" class="rabbitmq.listener.queue1Listener"/>
    <bean id="queue2Listener" class="rabbitmq.listener.queue2Listener"/>
    <bean id="queue3Listener" class="rabbitmq.listener.queue3Listener"/>
    <bean id="queue4Listener" class="rabbitmq.listener.queue4Listener"/>
    <bean id="queue5Listener" class="rabbitmq.listener.queue5Listener"/>
    <bean id="queue6Listener" class="rabbitmq.listener.queue6Listener"/>
    <bean id="queue7Listener" class="rabbitmq.listener.queue7Listener"/>

    <rabbit:listener-container connection-factory="connectionFactory" auto-declare="true">
        <rabbit:listener ref="queue0Listener" queue-names="spring_queue_0"/>
        <rabbit:listener ref="queue1Listener" queue-names="spring_queue_1"/>
        <rabbit:listener ref="queue2Listener" queue-names="spring_fanout_queue_1"/>
        <rabbit:listener ref="queue3Listener" queue-names="spring_fanout_queue_2"/>
        <rabbit:listener ref="queue4Listener" queue-names="spring_direct_queue_1"/>
        <rabbit:listener ref="queue5Listener" queue-names="spring_direct_queue_2"/>
        <rabbit:listener ref="queue6Listener" queue-names="spring_topic_queue_1"/>
        <rabbit:listener ref="queue7Listener" queue-names="spring_topic_queue_2"/>
    </rabbit:listener-container>


</beans>

消费者测试代码

package rabbitmq.listener;

import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageListener;

/**
 * @author : ZHX
 * @date : 2022/6/5 21:28
 * @description: 启动程序,监听器会区对应的queue中消费消息。
 */
public class queue0Listener implements MessageListener {
    
    

    @Override
    public void onMessage(Message message) {
    
    
        System.out.println(new String(message.getBody()));
    }
}
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

/**
 * @author : ZHX
 * @date : 2022/6/5 20:55
 * @description:
 */
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:beans.xml")
public class ConsumerTest {
    
    

    @Test
    public void testConsumer(){
    
    
        while (true){
    
    

        }
    }

}

6. Spring Boot 整合RabbitMQ

导入依赖

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-amqp</artifactId>
        </dependency>

生产者 application.yml配置文件

spring:
  rabbitmq:
    host: 192.168.205.132
    port: 5672
    virtual-host: order
    username: itcast
    password: 123456

配置exchange、queue、binding

package com.example.rabbitmq.config;

import org.springframework.amqp.core.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * @author : ZHX
 * @date : 2022/6/5 23:04
 * @description:
 */
@Configuration
public class RabbitMQConfig {
    
    

    public static final String TOPIC_EXCHANGE_NAME = "spring_topic_exchange";
    public static final String QUEUE_NAME = "spring_topic_queue_1";

    //1.交换机
    @Bean("exchange")
    public Exchange getExchange() {
    
    
        return ExchangeBuilder.topicExchange(TOPIC_EXCHANGE_NAME).durable(true).build();
    }

    //2.队列
    @Bean("queue")
    public Queue getQueue() {
    
    
        return QueueBuilder.durable(QUEUE_NAME).build();
    }

    //3.绑定关系
    @Bean("binding")
    public Binding getBinding(Queue queue, Exchange exchange) {
    
    
        return BindingBuilder.bind(queue).to(exchange).with("#.info").noargs();
    }
}

生产者发送消息代码

@SpringBootTest
@RunWith(SpringRunner.class)
public class ProducerTest {
    
    
    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Test
    public void testTopic(){
    
    
        rabbitTemplate.convertAndSend(RabbitMQConfig.TOPIC_EXCHANGE_NAME,"aa.info","hello");
    }
}

消费者消费消息代码

@Component
public class RabbitMQListener {
    
    
    @RabbitListener(queues = "spring_topic_queue_1")
    public void listenerQueue(Message message) {
    
    
        System.out.println(new String(message.getBody()));
    }
	
	//通过注解,创建交换机、队列、绑定关系
    @RabbitListener(bindings = {
    
    @QueueBinding(exchange = @Exchange(value = "topic.exchange",type = "topic"), value = @Queue("topic.queue"), key = "china.#")})
    public void receiveTopic(String msg) {
    
    
        System.out.println(msg);
    }
}

7. 消息转换器

Spring会把发送的消息序列化为字节发送给MQ, 接受消息时,将字节反序列化为Java对象。

JDK序列化 数据体积大、有安全漏洞、可读性差。

使用jackson序列化

  1. 依赖
        <dependency>
            <groupId>com.fasterxml.jackson.dataformat</groupId>
            <artifactId>jackson-dataformat-xml</artifactId>
        </dependency>
  1. 注入IOC容器
@Configuration
public class MessageConverterConfig {
    
    

    @Bean
    public MessageConverter messageConverter() {
    
    
        return new Jackson2JsonMessageConverter();
    }
}

8. RabbitMQ 高级特性

8.1 消息的可靠投递

RabbitMQ提供了两种模式,将消息是否成功发送告诉给发送方。

  • confirm 确认模式
  • return 退回模式

RabbitMQ消息投递的路径为:

producer->rabbingmq broker -> exchange-> queue->consumer

  • confirm模式: 消息从producer发到exchange,不管成功或失败都会执行ConfirmCallback函数 返回true/false。
  • return模式:消息从exchange到queue, 只有失败了才会执行returnCallback函数。

confirm模式

  1. 开启confirm模式
    在这里插入图片描述
  2. 设置ConfirmCallback函数
    在这里插入图片描述

return模式

  1. 开启 return模式
    在这里插入图片描述
  2. 设置函数
    在这里插入图片描述

8.2 Consumer ACK

acknowledge, 表示消费者收到消息后的确认方式。有三种:

  • 自动确认:none
  • 手动确认: manual
  • 根据异常情况确认:auto

自动确认模式:是指消息一旦被消费者接收到,就自动确认收到,并将相应的message从queue删除,但实际业务中很可能消息接收到,处理出现异常,那么该消息就丢失了。
手动确认模式:业务成功处理后调用channer.basicACK(),手动确认。如果业务处理失败,则调用channel.basicNack(方法,消息重回队列。

  1. 开启手动模式
    在这里插入图片描述
  2. 消费者代码
    在这里插入图片描述

8.3 消费端限流

  • 先将消费端的确认模式为手动模式
  • 设置perfetch :消费端每次拉取的消息数。

配置
在这里插入图片描述
代码
在这里插入图片描述

8.4 TTL

  • TTL:Time To Live 存活时间。
  • 当消息到达存活时间后,还没被消费会被自动删除。
  • RabbitMQ可以对消息设置过期时间,也可以对整个队列设置过期时间。

设置队列的TTL
在这里插入图片描述
设置单个消息的TTL
在这里插入图片描述

8.5 DLX

  • Dead Letter Exchange 死信交换机
  • 当消息超时成为死信后,可以重新被发送到DLX,

在这里插入图片描述
消息称为死信的三种情况

  1. 队列消息长度达到限制。
  2. 消费者拒收消息,basicNick/basicReject,并且不把消息重新放入原目标队列 requeue=false.
  3. 原消息队列存在过期设置,消息到达过期时间未被消费。

给队列绑定死信交换机

在这里插入图片描述

8.6 延迟队列

RabbitMQ 没有定时功能,但可以通过 TTL + DXL 实现。

9. 参考资料

https://www.bilibili.com/video/BV15k4y1k7Ep?p=22

https://www.bilibili.com/video/BV1cb4y1o7zz?p=2

https://www.rabbitmq.com/

猜你喜欢

转载自blog.csdn.net/ZHHX666/article/details/125120610