4、RabbitMQ的七种工作模式介绍

目录

一、Simple(简单模式)

[1.1 概念](#1.1 概念)

[1.2 代码实现](#1.2 代码实现)

消费者

运行结果

[二、Work Queue(工作队列)](#二、Work Queue(工作队列))

[2.1 概念](#2.1 概念)

[1.2 代码实现](#1.2 代码实现)

生产者

消费者

运行结果

三、Publish/Subscribe(发布/订阅模式)

[3.1 概念](#3.1 概念)

[3.2 代码实现](#3.2 代码实现)

生产者

消费者

运行结果

四、Routing(路由模式)

[4.1 概念](#4.1 概念)

[4.2 代码实现](#4.2 代码实现)

Constants类

生产者

消费者

运行结果

五、Topics(通配符模式)

[5.1 概念](#5.1 概念)

[5.2 代码实现](#5.2 代码实现)

生产者

消费者

运行结果

六、RPC(RPC通信)了解

[6.1 概念](#6.1 概念)

[6.2 代码实现](#6.2 代码实现)

客户端代码编写

编写服务器代码

运行结果:

[七、Publish Confirms(发布确认模式)](#七、Publish Confirms(发布确认模式))​​​​​​​

[publishing Messages Individually(单独确认)](#publishing Messages Individually(单独确认))

[Publishing Messages in Batches(批量确认)](#Publishing Messages in Batches(批量确认))

[Handling Publisher Confirms Asynchronously(异步确认)](#Handling Publisher Confirms Asynchronously(异步确认))


一、Simple(简单模式)

1.1 概念

**P:**生产者,也就是要发送消息的程序

**C:**消费者,消息的接受者

**Queue:**消息队列,图中Queue类似提个邮箱,可以缓存消息;生产者向其中投递消息,消费者从中取出消息。

特点:一个生产者P,一个消费者C,消息只能被消费一次,也称为点对点(Point-to-Point)模式

适用场景:消息只能被单个消费者处理

1.2 代码实现

消费者

java 复制代码
package rabbitmq.simple;

import com.rabbitmq.client.*;
import java.io.IOException;

public class ConsumerDemo {
    public static void main(String[] args) throws Exception {
        //1.创建连接
        // 创建一个ConnectionFactory实例来配置RabbitMQ连接
        ConnectionFactory connectionFactory = new ConnectionFactory();
        // 设置RabbitMQ服务器的主机地址
        connectionFactory.setHost("8.136.108.248");
        // 设置RabbitMQ服务器的端口号
        connectionFactory.setPort(5672);
        // 设置登录RabbitMQ服务器的用户名
        connectionFactory.setUsername("pinkboy");
        // 设置登录RabbitMQ服务器的密码
        connectionFactory.setPassword("123456");
        // 设置RabbitMQ服务器的虚拟主机
        connectionFactory.setVirtualHost("/");
        // 使用ConnectionFactory创建一个新的连接
        Connection connection = connectionFactory.newConnection();

        //2.创建Channel
        Channel channel = connection.createChannel();


        /**
         *3.声明一个队列
         *
         * @param channel RabbitMQ的通道,用于执行队列操作
         *
         * 此处使用了queueDeclare方法来声明一个名为"hello"的队列该方法的参数分别表示:
         * 1. 队列名称("hello"):指定要声明的队列的名称
         * 2. true:表示该队列是持久化的,意味着即使RabbitMQ服务重启,队列也会被保留
         * 3. false:表示该队列不是排他的,意味着该队列可以被所有通道共享
         * 4. false:表示该队列不会在使用后自动删除,需要手动删除
         * 5. null:表示不设置额外的参数
         *
         * 选择这些参数值的原因可能是希望创建一个持久化的、共享的队列,以便在不同的时间点和不同的消费者之间传递消息
         */
        channel.queueDeclare("hello", true, false, false, null);


        // 4.开始从名为"hello"的队列中消费消息
        channel.basicConsume("hello", true, new DefaultConsumer(channel) {
            /**
             * 处理接收到的消息
             *
             * @param consumerTag 消费者标签,用于标识消费者
             * @param envelope 包含消息路由信息的信封
             * @param properties 消息的属性,如内容类型、内容编码等
             * @param body 消息的主体内容,以字节数组形式表示
             * @throws IOException 如果处理消息时发生I/O错误
             */
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                // 打印消息的主体内容
                System.out.println("body:" + new String(body));
            }
        });


        Thread.sleep(2000);
        //5.关闭资源
        channel.close();
        connection.close();

    }
}

运行结果

启动生产者代码:

观察消息队列

启动消费者代码:

观察消息队列

二、Work Queue(工作队列)

2.1 概念

一个生产者P,多个消费者C1,C2 在多个消息的情况下,WorkQueue会将消息分派给不同的消费者,每个消费者都会接收到不同的消息

特点:消息不会重复,分配各不同的消费者

适用场景:集群环境中做异步处理

举个例子:12306短息通知服务,订票成功后,订单消息会发送到RabbitMQ,短信服务从RabbitMQ中获取订单信息,并发送通知信息(在短信服务之间进行任务分配)

1.2 代码实现

工作模式就是简单模式的增强版 和简单模式的区别就是 简单模式就一个消费者,工作模式支持多个消费者接收消息,消费者之间是竟争关系 每个消息只能被一个消费者接收

和简单模式代码差不多 为了展示多个消费者竞争的关系 生产者一次生产10条消息

常量类

java 复制代码
package rabbitmq.constant;

public class Constants {
    public static final String HOST = "8.136.108.248";
    public static final Integer PORT = 5672;
    public static final String USER_NAME = "pinkboy";
    public static final String PASSWORD = "123456";
    public static final String VIRTUAL_HOST = "/";

    //工作队列模式
    public static final String WORK_QUEUE = "work_queue";

}

生产者

java 复制代码
package rabbitmq.work;

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import rabbitmq.constant.Constants;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

public class Producer {
    public static void main(String[] args) throws Exception {
        //1、建立连接
        // 创建一个ConnectionFactory实例来配置RabbitMQ连接
        ConnectionFactory connectionFactory = new ConnectionFactory();
        // 设置RabbitMQ服务器的主机地址
        connectionFactory.setHost(Constants.HOST);
        // 设置RabbitMQ服务器的端口号
        connectionFactory.setPort(Constants.PORT);
        // 设置登录RabbitMQ服务器的用户名
        connectionFactory.setUsername(Constants.USER_NAME);
        // 设置登录RabbitMQ服务器的密码
        connectionFactory.setPassword(Constants.PASSWORD);
        // 设置RabbitMQ服务器的虚拟主机
        connectionFactory.setVirtualHost(Constants.VIRTUAL_HOST);
        // 使用ConnectionFactory创建一个新的连接
        Connection connection = connectionFactory.newConnection();

        //2、创建通道
        Channel channel = connection.createChannel();

        //3、声明交换机

        //4、声明队列
        /**
         * 声明一个队列
         *
         * @param channel RabbitMQ的通道,用于执行队列操作
         *
         * 此处使用了queueDeclare方法来声明一个名为"hello"的队列该方法的参数分别表示:
         * 1. 队列名称("hello"):指定要声明的队列的名称
         * 2. true:表示该队列是持久化的,意味着即使RabbitMQ服务重启,队列也会被保留
         * 3. false:表示该队列不是排他的,意味着该队列可以被所有通道共享
         * 4. false:表示该队列不会在使用后自动删除,需要手动删除
         * 5. null:表示不设置额外的参数
         *
         * 选择这些参数值的原因可能是希望创建一个持久化的、共享的队列,以便在不同的时间点和不同的消费者之间传递消息
         */
        channel.queueDeclare(Constants.WORK_QUEUE, true, false, false, null);

        //5、发送消息

        // 循环发送消息到 RabbitMQ 的 "hello" 队列中
        for (int i = 0; i < 10; i++) {
            // 构造消息内容
            String msg = "hello work queue ..." + i;
            /**
             * 参数1 表示交换机名称,因为使用默认交换机,所以为空字符串
             * 参数2 表示队列名称
             * 参数3 :消息的属性
             * 参数4:消息内容
             */
            channel.basicPublish("", Constants.WORK_QUEUE, null, msg.getBytes());
        }

        System.out.println("消息发送成功!");

        //6、释放资源
        channel.close();
        connection.close();
    }
}

消费者

两个消费者的代码是一样的

消费者1

java 复制代码
package rabbitmq.work;

import com.rabbitmq.client.*;
import rabbitmq.constant.Constants;

import java.io.IOException;

public class Consumer1 {
    public static void main(String[] args) throws Exception {
        //1、建立连接
        // 创建一个ConnectionFactory实例来配置RabbitMQ连接
        ConnectionFactory connectionFactory = new ConnectionFactory();
        // 设置RabbitMQ服务器的主机地址
        connectionFactory.setHost(Constants.HOST);
        // 设置RabbitMQ服务器的端口号
        connectionFactory.setPort(Constants.PORT);
        // 设置登录RabbitMQ服务器的用户名
        connectionFactory.setUsername(Constants.USER_NAME);
        // 设置登录RabbitMQ服务器的密码
        connectionFactory.setPassword(Constants.PASSWORD);
        // 设置RabbitMQ服务器的虚拟主机
        connectionFactory.setVirtualHost(Constants.VIRTUAL_HOST);
        // 使用ConnectionFactory创建一个新的连接
        Connection connection = connectionFactory.newConnection();

        //2.创建Channel
        Channel channel = connection.createChannel();

        channel.queueDeclare(Constants.WORK_QUEUE, true, false, false, null);
        channel.basicConsume(Constants.WORK_QUEUE, true, new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                // 打印消息的主体内容
                System.out.println("body:" + new String(body));
            }
        });

        System.out.println("Consumer1 启动成功!");

        //5.关闭资源
//        channel.close();
//        connection.close();
    }
}

消费者2

java 复制代码
package rabbitmq.work;

import com.rabbitmq.client.*;
import rabbitmq.constant.Constants;

import java.io.IOException;

public class Consumer2 {
    public static void main(String[] args) throws Exception {
        //1、建立连接
        // 创建一个ConnectionFactory实例来配置RabbitMQ连接
        ConnectionFactory connectionFactory = new ConnectionFactory();
        // 设置RabbitMQ服务器的主机地址
        connectionFactory.setHost(Constants.HOST);
        // 设置RabbitMQ服务器的端口号
        connectionFactory.setPort(Constants.PORT);
        // 设置登录RabbitMQ服务器的用户名
        connectionFactory.setUsername(Constants.USER_NAME);
        // 设置登录RabbitMQ服务器的密码
        connectionFactory.setPassword(Constants.PASSWORD);
        // 设置RabbitMQ服务器的虚拟主机
        connectionFactory.setVirtualHost(Constants.VIRTUAL_HOST);
        // 使用ConnectionFactory创建一个新的连接
        Connection connection = connectionFactory.newConnection();

        //2.创建Channel
        Channel channel = connection.createChannel();

        channel.queueDeclare(Constants.WORK_QUEUE, true, false, false, null);
        channel.basicConsume(Constants.WORK_QUEUE, true, new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                // 打印消息的主体内容
                System.out.println("body:" + new String(body));
            }
        });

        System.out.println("Consumer2 启动成功!");

        //5.关闭资源
//        channel.close();
//        connection.close();
    }
}

运行结果

生产者

生产者消息队列

消费者

为了避免第一个启动的消费者会将10条消息消费掉 需要先启动两个消费者,再去启动生产者

消费者1

消费者2

观察消息队列

可以看到管理页面中有两个消费者被显示

三、Publish/Subscribe(发布/订阅模式)

3.1 概念


Exchange: 交换机 (X).
作⽤: 生产者将消息发送到Exchange, 由交换机将消息按⼀定规则路由到⼀个或多个队列中(上图中⽣产 者将消息投递到队列中, 实际上这个在RabbitMQ中不会发生. )
RabbitMQ交换机有四种类型: fanout,direct, topic, headers, 不同类型有着不同的路由策略. AMQP协议里还有另外两种类型, System和⾃定义, 此处不再描述.
Fanout:广播,将消息交给所有绑定到交换机的队列( Publish/Subscribe模式)
一个生产者P,多个消费者C1,C2,X代表交换机消息复制多份,每个消费者接收相同的消息
生产者发送一条消息,经过交换机转发到多个不同的队列,多个不同的队列就有多个不同的消费者
适合场景:消息需要被多个消费者同时接收的场景.如:实时通知或者广播消息

3.2 代码实现

常量类

java 复制代码
public class Constants {
    public static final String HOST = "8.136.108.248";
    public static final Integer PORT = 5672;
    public static final String USER_NAME = "pinkboy";
    public static final String PASSWORD = "123456";
    public static final String VIRTUAL_HOST = "/";

//发布订阅模式
    public static final String FANOUT_EXCHANGE = "fanout_exchange";
    public static final String FANOUT_QUEUE1 = "fanout_queue1";
    public static final String FANOUT_QUEUE2 = "fanout_queue2";
}

这个模式需要创建交换机,并绑定队列和交换机

复制代码
//3、声明交换机
/**
 * 参数1:交换机名称
 * 参数2:交换机类型 Fanout类型 -> 广播机制
 * 参数3:是否持久化
 */
channel.exchangeDeclare(Constants.FANOUT_EXCHANGE, BuiltinExchangeType.FANOUT, true);

声明队列

复制代码
//4、声明队列
/**
 * 参数1:队列名称
 * 参数2:是否持久化
 * 参数3:是否独占队列,该队列只允许在该连接中访问,如果连接关闭队列则自动删除,如果将此参数设置true可用于临时队列的创建
 * 参数4:是否自动删除,当没有生产者或者消费者使用此队列,该队列会自动删除
 * 参数5:其他参数
 */
channel.queueDeclare(Constants.FANOUT_QUEUE1, true, false, false, null);
channel.queueDeclare(Constants.FANOUT_QUEUE2, true, false, false, null);

绑定队列和交换机

复制代码
//5、交换机和队列绑定
/**
 * 参数1:队列名称
 * 参数2:交换机名称
 * 参数3:路由键,绑定规则 如果交换机类型为fanout类型,routingKey设置为空字符串
 */
channel.queueBind(Constants.FANOUT_QUEUE1, Constants.FANOUT_EXCHANGE, "");
channel.queueBind(Constants.FANOUT_QUEUE2, Constants.FANOUT_EXCHANGE, "");

生产者

java 复制代码
package rabbitmq.fanout;

import com.rabbitmq.client.BuiltinExchangeType;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import rabbitmq.constant.Constants;

public class Producer {
    public static void main(String[] args) throws Exception {
        //1、建立连接
        ConnectionFactory connectionFactory = new ConnectionFactory();
        connectionFactory.setHost(Constants.HOST);
        connectionFactory.setPort(Constants.PORT);
        connectionFactory.setUsername(Constants.USER_NAME);
        connectionFactory.setPassword(Constants.PASSWORD);
        connectionFactory.setVirtualHost(Constants.VIRTUAL_HOST);
        Connection connection = connectionFactory.newConnection();

        //2、打开信道
        Channel channel = connection.createChannel();

        //3、声明交换机
        /**
         * 参数1:交换机名称
         * 参数2:交换机类型 Fanout类型 -> 广播机制
         * 参数3:是否持久化
         */
        channel.exchangeDeclare(Constants.FANOUT_EXCHANGE, BuiltinExchangeType.FANOUT, true);

        //4、声明队列
        /**
         * 参数1:队列名称
         * 参数2:是否持久化
         * 参数3:是否独占队列,该队列只允许在该连接中访问,如果连接关闭队列则自动删除,如果将此参数设置true可用于临时队列的创建
         * 参数4:是否自动删除,当没有生产者或者消费者使用此队列,该队列会自动删除
         * 参数5:其他参数
         */
        channel.queueDeclare(Constants.FANOUT_QUEUE1, true, false, false, null);
        channel.queueDeclare(Constants.FANOUT_QUEUE2, true, false, false, null);

        //5、交换机和队列绑定
        /**
         * 参数1:队列名称
         * 参数2:交换机名称
         * 参数3:路由键,绑定规则 如果交换机类型为fanout类型,routingKey设置为空字符串
         */
        channel.queueBind(Constants.FANOUT_QUEUE1, Constants.FANOUT_EXCHANGE, "");
        channel.queueBind(Constants.FANOUT_QUEUE2, Constants.FANOUT_EXCHANGE, "");

        //6、发布消息
        String msg = "hello fanout ...";
        channel.basicPublish(Constants.FANOUT_EXCHANGE, "", null, msg.getBytes());
        System.out.println("消息发送成功!");

        //7、释放资源
        channel.close();
        connection.close();
    }
}

消费者

消费者1

java 复制代码
package rabbitmq.fanout;

import com.rabbitmq.client.*;
import rabbitmq.constant.Constants;

import java.io.IOException;

public class Consumer1 {
    public static void main(String[] args) throws Exception {
        //1、建立连接
        ConnectionFactory connectionFactory = new ConnectionFactory();
        connectionFactory.setHost(Constants.HOST);
        connectionFactory.setPort(Constants.PORT);
        connectionFactory.setUsername(Constants.USER_NAME);
        connectionFactory.setPassword(Constants.PASSWORD);
        connectionFactory.setVirtualHost(Constants.VIRTUAL_HOST);
        Connection connection = connectionFactory.newConnection();
        //2、创建信道
        Channel channel = connection.createChannel();

        //3、声明队列
        channel.queueDeclare(Constants.FANOUT_QUEUE1, true, false, false, null);

        //4、消费消息
        channel.basicConsume(Constants.FANOUT_QUEUE1, true, new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                // 打印消息的主体内容
                System.out.println("body:" + new String(body));
            }
        });

        System.out.println("Consumer1 启动成功!");

    }
}

消费者2

java 复制代码
package rabbitmq.fanout;


import com.rabbitmq.client.*;
import rabbitmq.constant.Constants;

import java.io.IOException;

public class Consumer2 {
    public static void main(String[] args) throws Exception {
        //1、建立连接
        ConnectionFactory connectionFactory = new ConnectionFactory();
        connectionFactory.setHost(Constants.HOST);
        connectionFactory.setPort(Constants.PORT);
        connectionFactory.setUsername(Constants.USER_NAME);
        connectionFactory.setPassword(Constants.PASSWORD);
        connectionFactory.setVirtualHost(Constants.VIRTUAL_HOST);
        Connection connection = connectionFactory.newConnection();
        //2、创建信道
        Channel channel = connection.createChannel();

        //3、声明队列
        channel.queueDeclare(Constants.FANOUT_QUEUE2, true, false, false, null);

        //4、消费消息
        channel.basicConsume(Constants.FANOUT_QUEUE2, true, new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                // 打印消息的主体内容
                System.out.println("body:" + new String(body));
            }
        });

        System.out.println("Consumer2 启动成功!");

    }
}

运行结果

启动生产者

观察消息队列

fanout_queue1 和 fanout_queue2 分别有了1条消息

Exchange中多了队列的绑定关系

启动两个消费者

观察消息队列

四、Routing(路由模式)

4.1 概念

路由模式是发布订阅模式的变种,在发布订阅基础上,增加路由key

发布订阅模式是无条件的将所有消息分发给所有的消费者,路由模式是Exchange根据RoutingKey的规则,将数据筛选后发给对应的消费者队列

适合场景:需要根据特定规则分发消息的场景
比如系统打印日志, 日志等级分为error, warning, info,debug, 就可以通过这种模式,把不同的日志发
送到不同的队列, 最终输出到不同的⽂件
Exchange(交换机)只负责转发消息,不具备存储消息的能力,因此如果没有任何队列与Exchange绑定,或者没有符合路由规则的队列,那么消息就会丢失
RoutingKey:路由键.生产者将消息发给交换器时,指定的一个字符串,用来告诉交换机应该如何处理这个消息.
BindingKey:绑定.RabbitMQ中通过Binding(绑定)将交换器与队列关联起来,在绑定的时候一般会指定一个BindingKey,这样RabbitMQ就知道如何正确地将消息路由到队列了.

队列和交换机的绑定,不能是任意的绑定了,而是要制定了一个BindKey(RoutingKey的一种)消息的发送方在向Exchange发送消息时也需要指定消息的RoutingKey

Exchange也不再把消息交给每一个绑定的key,而是根据消息的RountingKey进行判断,只要队列的BindingKey和发送消息的RoutingKey完全一致,才会接收到消息

创建交换机,定义交换机类型为BuiltinExchangeType.DIRECT

4.2 代码实现

Constants类

java 复制代码
public class Constants {
    public static final String HOST = "8.136.108.248";
    public static final Integer PORT = 5672;
    public static final String USER_NAME = "pinkboy";
    public static final String PASSWORD = "123456";
    public static final String VIRTUAL_HOST = "/";

    //路由模式
    public static final String DIRECT_EXCHANGE = "direct.exchange";
    public static final String DIRECT_QUEUE1 = "direct.queue1";
    public static final String DIRECT_QUEUE2 = "direct.queue2";
}

生产者

java 复制代码
package rabbitmq.direct;

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import rabbitmq.constant.Constants;

public class Producer {
    public static void main(String[] args) throws Exception {
        //1. 建立连接
        ConnectionFactory connectionFactory = new ConnectionFactory();
        connectionFactory.setHost(Constants.HOST);
        connectionFactory.setPort(Constants.PORT);
        connectionFactory.setUsername(Constants.USER_NAME);
        connectionFactory.setPassword(Constants.PASSWORD);
        connectionFactory.setVirtualHost(Constants.VIRTUAL_HOST);
        Connection connection = connectionFactory.newConnection();

        //2.  创建信道
        Channel channel = connection.createChannel();

        //3.  声明交换机
        /**
         * 参数1:交换机名称
         * 参数2:交换机类型
         * 参数3:是否持久化
         * 参数4:是否自动删除
         * 参数5:其他参数
         */
        channel.exchangeDeclare(Constants.DIRECT_EXCHANGE, "direct", true, false, null);

        //4.  声明队列
        /**
         * 参数1:队列名称
         * 参数2:是否持久化
         * 参数3:是否独占队列,该队列只允许在该连接中访问,如果连接关闭队列则自动删除,如果将此参数设置true可用于临时队列的创建
         * 参数4:是否自动删除,队列不再使用时是否自动删除此队列,如果将此参数和参数2设置为true就可以实现临时队列(队列不用了就自动删除)
         * 参数5:其他参数
         */
        channel.queueDeclare(Constants.DIRECT_QUEUE1, true, false, false, null);
        channel.queueDeclare(Constants.DIRECT_QUEUE2, true, false, false, null);

        //5、交换机和队列绑定
        /**
         * 参数1:队列名称
         * 参数2:交换机名称
         * 参数3:路由键,绑定规则
         */
        channel.queueBind(Constants.DIRECT_QUEUE1, Constants.DIRECT_EXCHANGE, "a");
        channel.queueBind(Constants.DIRECT_QUEUE2, Constants.DIRECT_EXCHANGE, "a");
        channel.queueBind(Constants.DIRECT_QUEUE2, Constants.DIRECT_EXCHANGE, "b");
        channel.queueBind(Constants.DIRECT_QUEUE2, Constants.DIRECT_EXCHANGE, "c");

        //6、发布消息
        String msg_a = "hello direct my routingKey is a...";
        channel.basicPublish(Constants.DIRECT_EXCHANGE, "a", null, msg_a.getBytes());

        String msg_b = "hello direct my routingKey is b...";
        channel.basicPublish(Constants.DIRECT_EXCHANGE, "b", null, msg_b.getBytes());

        String msg_c = "hello direct my routingKey is c...";
        channel.basicPublish(Constants.DIRECT_EXCHANGE, "c", null, msg_c.getBytes());
        System.out.println("消息发送成功!");

        //7、释放资源
        channel.close();
        connection.close();
    }
}

消费者

java 复制代码
package rabbitmq.direct;

import com.rabbitmq.client.*;
import rabbitmq.constant.Constants;

import java.io.IOException;

public class Consumer1 {
    public static void main(String[] args) throws Exception {
        //1、建立连接
        ConnectionFactory connectionFactory = new ConnectionFactory();
        connectionFactory.setHost(Constants.HOST);
        connectionFactory.setPort(Constants.PORT);
        connectionFactory.setUsername(Constants.USER_NAME);
        connectionFactory.setPassword(Constants.PASSWORD);
        connectionFactory.setVirtualHost(Constants.VIRTUAL_HOST);
        Connection connection = connectionFactory.newConnection();
        //2、创建信道
        Channel channel = connection.createChannel();

        //3、声明队列
        channel.queueDeclare(Constants.DIRECT_QUEUE1, true, false, false, null);

        //4、消费消息
        channel.basicConsume(Constants.DIRECT_QUEUE1, true, new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                // 打印消息的主体内容
                System.out.println("body:" + new String(body));
            }
        });

        System.out.println("Consumer1 启动成功!");

    }
}
java 复制代码
package rabbitmq.direct;

import com.rabbitmq.client.*;
import rabbitmq.constant.Constants;

import java.io.IOException;

public class Consumer2 {
    public static void main(String[] args) throws Exception {
        //1、建立连接
        ConnectionFactory connectionFactory = new ConnectionFactory();
        connectionFactory.setHost(Constants.HOST);
        connectionFactory.setPort(Constants.PORT);
        connectionFactory.setUsername(Constants.USER_NAME);
        connectionFactory.setPassword(Constants.PASSWORD);
        connectionFactory.setVirtualHost(Constants.VIRTUAL_HOST);
        Connection connection = connectionFactory.newConnection();
        //2、创建信道
        Channel channel = connection.createChannel();

        //3、声明队列
        channel.queueDeclare(Constants.DIRECT_QUEUE2, true, false, false, null);

        //4、消费消息
        channel.basicConsume(Constants.DIRECT_QUEUE2, true, new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                // 打印消息的主体内容
                System.out.println("body:" + new String(body));
            }
        });

        System.out.println("Consumer1 启动成功!");
    }
}

运行结果

启动生产者

观察消息队列界面

exchange下队列和Routing Key的绑定关系

启动消费者

观察消息队列界面

五、Topics(通配符模式)

5.1 概念

在路由模式上进行了升级,在routingKey的基础上,增加可通配符的功能,适之更加灵活

Topic和Routing的基本原同,即:生产者将消息发送给交换机,交换机根据RoutingKey将消息转发给RoutingKey匹配的队列,类似于正则表达式的方式来定义RoutingKey的模式.

适合场景:需要灵活匹配和过滤消息的场景

Topic和Routing模式的区别:

1、topic模式使用的交换机类型为topic(Rounting模式使用的交换机类型为direct)

2、topic类型的交换机在匹配规则上进行了扩展,BingingKey支持通配符匹配(direct类型的将换季路由规则是BingKey和RoutingKey完全匹配)

在topic类型的交换机在匹配规则上有些要求:

1.RoutingKey是一系列由点(.)分隔的单词,比如"stock.usd.nyse","nyse.vmw","quick.organge.rabbit"

  1. BingdingKey和RountingKey,也是点(.)分隔的字符串

  2. BingdingKey中可以存在两种特殊的字符串,用于模糊匹配

* 表示一个单词

# 表示0-N个单词

举个例子:
Binding Key 为"d.a.b" 会同时路由到Q1 和Q2
Binding Key 为"d.a.f" 会路由到Q1
Binding Key 为"c.e.f" 会路由到Q2
Binding Key 为"d.b.f" 会被丢弃, 或者返回给⽣产者(需要设置mandatory参数)

5.2 代码实现

Constants类

java 复制代码
public class Constants {
    public static final String HOST = "8.136.108.248";
    public static final Integer PORT = 5672;
    public static final String USER_NAME = "pinkboy";
    public static final String PASSWORD = "123456";
    public static final String VIRTUAL_HOST = "/";


    //通配符模式
    public static final String TOPIC_EXCHANGE = "topic.exchange";
    public static final String TOPIC_QUEUE1 = "topic.queue1";
    public static final String TOPIC_QUEUE2 = "topic.queue2";

}

生产者

创建交换机类型为BuiltinExchangeType.TOPIC

//3.声明交换机

channel.exchangeDeclare(Constants.TOPIC_EXCHANGE, BuiltinExchangeType.TOPIC, true, false, null);

声明队列

复制代码
//4.声明队列
channel.queueDeclare(Constants.TOPIC_QUEUE1, true, false, false, null);
channel.queueDeclare(Constants.TOPIC_QUEUE2, true, false, false, null);

绑定交换机和队列

复制代码
//5.绑定交换机和队列
channel.queueBind(Constants.TOPIC_QUEUE1, Constants.TOPIC_EXCHANGE, "*.a.*");
channel.queueBind(Constants.TOPIC_QUEUE2, Constants.TOPIC_EXCHANGE, "*.*.b");
channel.queueBind(Constants.TOPIC_QUEUE2, Constants.TOPIC_EXCHANGE, "c.#");
java 复制代码
package rabbitmq.topic;

import com.rabbitmq.client.BuiltinExchangeType;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import rabbitmq.constant.Constants;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

public class Producer {
    public static void main(String[] args) throws IOException, TimeoutException {
        //1.建立连接
        ConnectionFactory connectionFactory = new ConnectionFactory();
        connectionFactory.setHost(Constants.HOST);
        connectionFactory.setPort(Constants.PORT);
        connectionFactory.setUsername(Constants.USER_NAME);
        connectionFactory.setPassword(Constants.PASSWORD);
        connectionFactory.setVirtualHost(Constants.VIRTUAL_HOST);
        Connection connection = connectionFactory.newConnection();

        //2.开启信道
        Channel channel = connection.createChannel();

        //3.声明交换机
        channel.exchangeDeclare(Constants.TOPIC_EXCHANGE, BuiltinExchangeType.TOPIC, true, false, null);

        //4.声明队列
        channel.queueDeclare(Constants.TOPIC_QUEUE1, true, false, false, null);
        channel.queueDeclare(Constants.TOPIC_QUEUE2, true, false, false, null);

        //5.绑定交换机和队列
        channel.queueBind(Constants.TOPIC_QUEUE1, Constants.TOPIC_EXCHANGE, "*.a.*");
        channel.queueBind(Constants.TOPIC_QUEUE2, Constants.TOPIC_EXCHANGE, "*.*.b");
        channel.queueBind(Constants.TOPIC_QUEUE2, Constants.TOPIC_EXCHANGE, "c.#");


        //6、发布消息
        String msg_a = "hello topic my routingKey is ae.a.f...";
        channel.basicPublish(Constants.TOPIC_EXCHANGE, "ae.a.f", null, msg_a.getBytes());

        String msg_b = "hello topic my routingKey is ef.a.b...";
        channel.basicPublish(Constants.TOPIC_EXCHANGE, "ef.a.b", null, msg_b.getBytes());

        String msg_c = "hello topic my routingKey is c...";
        channel.basicPublish(Constants.TOPIC_EXCHANGE, "c.ef.d", null, msg_c.getBytes());

        System.out.println("消息发送成功!");
    }
}

消费者

java 复制代码
package rabbitmq.topic;

import com.rabbitmq.client.*;
import rabbitmq.constant.Constants;

import java.io.IOException;

public class Consumer1 {
    public static void main(String[] args) throws Exception {
        //1、建立连接
        // 创建一个ConnectionFactory实例来配置RabbitMQ连接
        ConnectionFactory connectionFactory = new ConnectionFactory();
        // 设置RabbitMQ服务器的主机地址
        connectionFactory.setHost(Constants.HOST);
        // 设置RabbitMQ服务器的端口号
        connectionFactory.setPort(Constants.PORT);
        // 设置登录RabbitMQ服务器的用户名
        connectionFactory.setUsername(Constants.USER_NAME);
        // 设置登录RabbitMQ服务器的密码
        connectionFactory.setPassword(Constants.PASSWORD);
        // 设置RabbitMQ服务器的虚拟主机
        connectionFactory.setVirtualHost(Constants.VIRTUAL_HOST);
        // 使用ConnectionFactory创建一个新的连接
        Connection connection = connectionFactory.newConnection();

        //2.创建Channel
        Channel channel = connection.createChannel();

        channel.queueDeclare(Constants.TOPIC_QUEUE1, true, false, false, null);
        channel.basicConsume(Constants.TOPIC_QUEUE1, true, new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                // 打印消息的主体内容
                System.out.println("body:" + new String(body));
            }
        });

        System.out.println("Consumer1 启动成功!");
    }
}
java 复制代码
public class Consumer2 {
    public static void main(String[] args) throws Exception {
        //1、建立连接
        // 创建一个ConnectionFactory实例来配置RabbitMQ连接
        ConnectionFactory connectionFactory = new ConnectionFactory();
        // 设置RabbitMQ服务器的主机地址
        connectionFactory.setHost(Constants.HOST);
        // 设置RabbitMQ服务器的端口号
        connectionFactory.setPort(Constants.PORT);
        // 设置登录RabbitMQ服务器的用户名
        connectionFactory.setUsername(Constants.USER_NAME);
        // 设置登录RabbitMQ服务器的密码
        connectionFactory.setPassword(Constants.PASSWORD);
        // 设置RabbitMQ服务器的虚拟主机
        connectionFactory.setVirtualHost(Constants.VIRTUAL_HOST);
        // 使用ConnectionFactory创建一个新的连接
        Connection connection = connectionFactory.newConnection();

        //2.创建Channel
        Channel channel = connection.createChannel();

        channel.queueDeclare(Constants.TOPIC_QUEUE2, true, false, false, null);
        channel.basicConsume(Constants.TOPIC_QUEUE2, true, new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                // 打印消息的主体内容
                System.out.println("body:" + new String(body));
            }
        });

        System.out.println("Consumer1 启动成功!");
    }
}

运行结果

生产者

观察消息队列界面

消费者

六、RPC(RPC通信)了解

6.1 概念

RPC(Remote Procedure Call) 即远程调用 它是一种通过网络从远程计算机上请求服务,而不是需要了解底层网络的技术,类似HTTP远程调用

RabbitMQ实现RPC通信的过程,大概率是通过两个队列实现一个可回调的过程

大概流程

1.客户端发消息到一个指定的队列,并在消息属性中设置replyTo字段,这个字段指定一个回调队列,服务端处理后,会把响应结果发送到这个队列

2.服务端接收到请求后,处理请求并发送响应消息到replyTo指定的回调队列

3.客户端在回调队列上等待响应消息,一旦收到响应,客户端会检查消息的correlationId属性,以确保它是所期望的响应

客户端:

1 发送请求(携带replyTo,CorrelationID)

2 接收响应(校验correlationID)

服务端:

1 接受请求,进行响应

2 发送响应(按照客户端指定的replyTo,设置correlationID)

6.2 代码实现

客户端代码编写

1、声明两个队列 RPC_REQUEST_QUEUE和RPC_RESPONSE_QUEUE,声明本次请求的唯一标志correlationID

2、将RPC_RESPONSE_QUEUE和correlationID配置到要发送的消息队列中

3、使用阻塞队列来阻塞当前进程,监听回调队列中的消息,把请求放到阻塞队列中

4、阻塞队列有消息后,主线程被唤醒,打印返回内容

Constants类

java 复制代码
public class Constants {
    public static final String HOST = "8.136.108.248";
    public static final Integer PORT = 5672;
    public static final String USER_NAME = "pinkboy";
    public static final String PASSWORD = "123456";
    public static final String VIRTUAL_HOST = "/";

    //rpc 模式
    public static final String RPC_REQUEST_QUEUE = "rpc.request.queue";
    public static final String RPC_RESPONSE_QUEUE = "rpc.response.queue";

}

客户端代码

java 复制代码
package rabbitmq.rpc;

import com.rabbitmq.client.*;
import rabbitmq.constant.Constants;

import java.io.IOException;
import java.util.UUID;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingDeque;


/**
 * rpc 客户端
 * 1. 发送请求
 * 2. 等待响应
 */
public class RpcClient {
    public static void main(String[] args) throws Exception { 
        //1、建立连接
        ConnectionFactory connectionFactory = new ConnectionFactory();
        connectionFactory.setHost(Constants.HOST);
        connectionFactory.setPort(Constants.PORT);
        connectionFactory.setUsername(Constants.USER_NAME);
        connectionFactory.setPassword(Constants.PASSWORD);
        connectionFactory.setVirtualHost(Constants.VIRTUAL_HOST);
        Connection connection = connectionFactory.newConnection();

        //2、开启信道
        Channel channel = connection.createChannel();
        channel.queueDeclare(Constants.RPC_REQUEST_QUEUE, true, false, false, null);
        channel.queueDeclare(Constants.RPC_RESPONSE_QUEUE, true, false, false, null);

        //3、发送请求
        String msg = "hello rpc...";
        //设置请求唯一标识
        //设置请求的相关属性
        // 生成一个唯一的关联ID,用于跟踪请求和响应
        String correlationID = UUID.randomUUID().toString();

        // 创建并配置AMQP基本属性,设置消息的关联ID和回复队列
        AMQP.BasicProperties prop = new AMQP.BasicProperties()
                .builder()
                .correlationId(correlationID)
                .replyTo(Constants.RPC_RESPONSE_QUEUE)
                .build();

        // 发布消息到指定的请求队列,携带配置的属性和消息体
        channel.basicPublish("", Constants.RPC_REQUEST_QUEUE, prop, msg.getBytes());

        //4、接收响应
        //使用阻塞队列,存储响应信息
        final ArrayBlockingQueue<String> response = new ArrayBlockingQueue<>(1);

        channel.basicConsume(Constants.RPC_RESPONSE_QUEUE, true, new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                String respMsg = new String(body);
                System.out.println("接收到回调消息:" + respMsg);
                if (correlationID.equals(properties.getCorrelationId())) {
                    //如果correlationID校验一致,则将响应信息保存在response中
                    response.offer(respMsg);
                }
            }
        });

        /**
         * 阻塞等待响应
         */
        String take = response.take();
        System.out.println("[RPC Client 响应结果]:" + take);
    }
}

编写服务器代码

java 复制代码
package rabbitmq.rpc;

import com.rabbitmq.client.*;
import rabbitmq.constant.Constants;

import java.io.IOException;

/**
 * 1、接受请求
 * 2、发送响应
 */
public class RpcServer {
    public static void main(String[] args) throws Exception {
        //1、建立连接
        ConnectionFactory connectionFactory = new ConnectionFactory();
        connectionFactory.setHost(Constants.HOST);
        connectionFactory.setPort(Constants.PORT);
        connectionFactory.setUsername(Constants.USER_NAME);
        connectionFactory.setPassword(Constants.PASSWORD);
        connectionFactory.setVirtualHost(Constants.VIRTUAL_HOST);
        Connection connection = connectionFactory.newConnection();

        //2、开启信道
        Channel channel = connection.createChannel();
        //3、接受请求


        channel.basicConsume(Constants.RPC_REQUEST_QUEUE, false, new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                String request = new String(body);
                System.out.println("接收到请求:" + request);
                String response = "针对request:" + request + ",响应成功";
              
                AMQP.BasicProperties basicProperties = new AMQP.BasicProperties()
                        .builder()
                        .correlationId(properties.getCorrelationId())
                        .build();
                
                channel.basicPublish("", Constants.RPC_RESPONSE_QUEUE, basicProperties, response.getBytes());
                channel.basicAck(envelope.getDeliveryTag(), false);
            }
        });

    }
}

运行结果:

启动客户端

观察消息队列界面

启动服务器端

客户端输出消息

观察消息队列界面

七、Publish Confirms(发布确认模式)

作为消息中间件,都会面临消息丢失的问题

消息丢失大概分为三种情况

1、生产者问题 因为应用程序故、障网络抖动等原因,生产者没有成功想broker发送消息

2、消息中间件自身问题,生产者成功发送给Broker 但是Broker没有把消息保存好,导致消息丢失

3、消费者问题,Broker发送到消费者,消费者在消费时,因没处理好,导致消费者失败的消息从队列中删除了

针对问题1 可以采用发确认(Publisher Cofirms)机制实现

生产者将信道设置成confirm(确认)模式,一但信道进入confirm模式,所有在该信道上面发布的消息都是会被指派一个唯一的ID(从1开始),一但消息被投递到所有匹配的队列之后,RabbitMq就会发送一个确认给生产者(包括消息的唯一ID)这就使得生产者知道消息已经正确到达目的队列了,如果消息和队列是可持久化的,那么消息确认会在将消息写入到磁盘之后发出,broker回传给生产者的确认消息中

deliveryTag包包含了消息的序号,此外broker也可以设置channel basicAck方法中的multiple参数,表示到这个序号之前的所有消息都已经得到处理

发送方确认机制最大的好处是他是异步的,生产者可以同时发布消息和等待信道返回确认消息

1、当消息最终得到确认之后,生产者可以通过回调方法来处理该确认消息

2、如果RabbitMQ因为自身内部错误导致消息丢失,就会发送一条nack(Basic.Nack)命令,生产者同样可以在回调方法中处理该nack命令

使用发送确认机制,必须要将信道设置成confirm(确认)模式

java 复制代码
package rabbitmq.comfirms;

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.ConfirmListener;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import rabbitmq.constant.Constants;

import java.io.IOException;
import java.util.Collections;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.concurrent.TimeoutException;

public class PublisherConfirms {

    private static final Integer MESSAGE_COUNT = 200;

    static Connection createConnection() throws Exception {
        ConnectionFactory connectionFactory = new ConnectionFactory();
        connectionFactory.setHost(Constants.HOST);
        connectionFactory.setPort(Constants.PORT);
        connectionFactory.setUsername(Constants.USER_NAME);
        connectionFactory.setPassword(Constants.PASSWORD);
        connectionFactory.setVirtualHost(Constants.VIRTUAL_HOST);
        return connectionFactory.newConnection();
    }

    public static void main(String[] args) throws Exception {
        // 发布确认
        publishingMessagesIndividually();

        // 批量发布确认
        publishingMessagesInBatchs();

        // 异步发布确认
        publishingMessagesAsynchronously();
    }

    private static void publishingMessagesAsynchronously() throws Exception {
        try (Connection connection = createConnection()) {
            //1 、创建信道
            Channel channel = connection.createChannel();
            //2、设置信道为confirm模式
            channel.confirmSelect();
            //3、声明队列
            channel.queueDeclare(Constants.PUBLISH_CONFIRM_QUEUE3, true, false, false, null);
            //4、监听confirm消息
            //集合中存储的是未确认的消息ID
            long start = System.currentTimeMillis();
            SortedSet<Long> confirmSeqNo = Collections.synchronizedSortedSet(new TreeSet<>());
            channel.addConfirmListener(new ConfirmListener() {
                @Override
                public void handleAck(long deliveryTag, boolean multiple) throws IOException {
                    if (multiple) {
                        confirmSeqNo.headSet(deliveryTag + 1).clear();
                    } else {
                        confirmSeqNo.remove(deliveryTag);
                    }
                }

                @Override
                public void handleNack(long deliveryTag, boolean multiple) throws IOException {
                    if (multiple) {
                        confirmSeqNo.headSet(deliveryTag + 1).clear();
                    } else {
                        confirmSeqNo.remove(deliveryTag);
                    }
                    //业务需要根据实际场景进行处理,比如重发
                }
            });
            //5.发送消息
            for (int i = 0; i < MESSAGE_COUNT; i++) {
                String msg = "hello publish confirms" + i;
                long seqNO = channel.getNextPublishSeqNo();
                channel.basicPublish("", Constants.PUBLISH_CONFIRM_QUEUE3, null, msg.getBytes());
                confirmSeqNo.add(seqNO);
            }
            while (!confirmSeqNo.isEmpty()) {
                Thread.sleep(10);
            }
            long end = System.currentTimeMillis();
            System.out.printf("异步确认策略,消息条数:%d,耗时:%d ms \n", MESSAGE_COUNT, (end - start));
        }
    }

    private static void publishingMessagesInBatchs() throws Exception {
        try (Connection connection = createConnection()) {
            //1 、创建信道
            Channel channel = connection.createChannel();
            //2、设置信道为confirm模式
            channel.confirmSelect();
            //3、声明队列
            channel.queueDeclare(Constants.PUBLISH_CONFIRM_QUEUE2, true, false, false, null);
            //4、发送消息
            long start = System.currentTimeMillis();
            int batchSize = 100;
            int outStandingMessageCount = 0;
            for (int i = 0; i < MESSAGE_COUNT; i++) {
                String msg = "hello publish confirms" + i;
                channel.basicPublish("", Constants.PUBLISH_CONFIRM_QUEUE2, null, msg.getBytes());
                outStandingMessageCount++;
                if (outStandingMessageCount == batchSize) {
                    //6、等待确认
                    channel.waitForConfirms(5000);
                    outStandingMessageCount = 0;
                }
            }
            if (outStandingMessageCount > 0) {
                channel.waitForConfirms(5000);
            }
            long end = System.currentTimeMillis();
            System.out.printf("批量确认策略,消息条数:%d,耗时:%d ms \n", MESSAGE_COUNT, (end - start));
        }
    }

    private static void publishingMessagesIndividually() throws Exception {
        try (Connection connection = createConnection()) {
            //1 、创建信道
            Channel channel = connection.createChannel();
            //2、设置信道为confirm模式
            channel.confirmSelect();
            //3、声明队列
            channel.queueDeclare(Constants.PUBLISH_CONFIRM_QUEUE1, true, false, false, null);

            //4、发送消息
            long start = System.currentTimeMillis();
            for (int i = 0; i < MESSAGE_COUNT; i++) {
                String msg = "hello publish confirms" + i;
                channel.basicPublish("", Constants.PUBLISH_CONFIRM_QUEUE1, null, msg.getBytes());
                //5、等待确认
                channel.waitForConfirms(5000);
            }
            long end = System.currentTimeMillis();

            System.out.printf("单独确认策略,消息条数:%d,耗时:%d ms \n", MESSAGE_COUNT, (end - start));
        } catch (IOException e) {
            throw new RuntimeException(e);
        } catch (TimeoutException | InterruptedException e) {
            throw new RuntimeException(e);
        }
    }
}

publishing Messages Individually(单独确认)


观察上面代码, 会发现这种策略是每发送⼀条消息后就调用channel.waitForConfirms方法,之后等待服务端的确认, 这实际上是⼀种串行同步等待的方式. 尤其对于持久化的消息来说, 需要等待消息确
认存储在磁盘之后才会返回(调用Linux内核的fsync方法). 但是发布确认机制是⽀持异步的. 可以⼀边发送消息, ⼀边等待消息确认。

Publishing Messages in Batches(批量确认)


相比于单独确认策略, 批量确认极大地提升了confirm的效率, 缺点是出现Basic.Nack或者超时时, 我们 不清楚具体哪条消息出了问题. 客⼾端需要将这⼀批次的消息全部重发, 这会带来明显的重复消息数量.
当消息经常丢失时,批量确认的性能应该是不升反降的.

Handling Publisher Confirms Asynchronously(异步确认)


异步confirm方法的编程实现最为复杂. Channel 接⼝提供了⼀个方法addConfirmListener. 这个方法
可以添加ConfirmListener 回调接口.
ConfirmListener 接口中包含两个方法: handleAck(long deliveryTag, boolean multiple) 和 handleNack(long deliveryTag, boolean multiple) , 分别对应处理 RabbitMQ发送给⽣产者的ack和nack.
deliveryTag 表示 发送消息的序号. multiple 表示 是否批量确认.
我们需要为每⼀个Channel 维护⼀个已发送消息的序号集合. 当收到RabbitMQ的confirm 回调时, 从集
合中删除对应的消息. 当Channel开启confirm模式后, channel上发送消息都会附带⼀个从1开始递增的
deliveryTag序号. 我们可以使⽤SortedSet 的有序性来维护这个已发消息的集合.

  1. 当收到ack时, 从序列中删除该消息的序号. 如果为批量确认消息, 表示小于等于当前序号
    deliveryTag的消息都收到了, 则清除对应集合
  2. 当收到nack时, 处理逻辑类似, 不过 需要结合具体的业务情况, 进型消息重发等操作.
相关推荐
国科安芯1 天前
ASL706S:让 MCU 不再“失忆跑飞“的守护芯片
分布式·单片机·嵌入式硬件·安全·fpga开发·架构
阿无,1 天前
RabbitMQ面试题
分布式·rabbitmq
盛世宏博智慧档案2 天前
全国100个分布式机房环境温湿度智能监测系统建设方案
分布式·机房·温湿度
六bring个六2 天前
分布式设备连接对端失败问题分析
分布式·分布式软总线
Broccoli523026652 天前
FastAPI 中间件
中间件·fastapi
天涯明月19932 天前
ray深度研究报告
大数据·人工智能·分布式·ray
Rain的Java大神之路2 天前
介绍一下分布式事务
java·分布式·后端·spring·spring cloud·架构·springcloud
大菠萝爱上小西瓜3 天前
【大数据实战】Spark 2.2.2 完全分布式集群搭建
大数据·分布式·spark
肠畔码农3 天前
深度解析 RocketMQ 消费端限流与重平衡(Rebalance):分布式队列分配与流控防雪崩本质
分布式·rocketmq
紫水木鱼3 天前
物联网通讯协议_MQTT_持续更新
物联网·中间件