消息队列-RabbitMQ(1)

文章目录

  • [一. MQ的应用场景](#一. MQ的应用场景)
    • [1. 异步解耦](#1. 异步解耦)
    • [2. 流量削峰](#2. 流量削峰)
    • [3. 消息订阅分发](#3. 消息订阅分发)
    • [4. 延迟通知](#4. 延迟通知)
    • [二. RabbitMQ的结构分析与工作流程](#二. RabbitMQ的结构分析与工作流程)
    • [1. AMQP协议](#1. AMQP协议)
    • [2. 客户端与服务器](#2. 客户端与服务器)
    • [3. Connection](#3. Connection)
    • [4. Channel](#4. Channel)
    • [5. Virtual host](#5. Virtual host)
    • [6. Exchange](#6. Exchange)
    • [7. Queue](#7. Queue)
    • [8. 工作流程](#8. 工作流程)
      • [(1) 创建连接](#(1) 创建连接)
      • [(2) 声明交换机和队列](#(2) 声明交换机和队列)
      • [(3) 发送消息](#(3) 发送消息)
      • [(4) 存储消息](#(4) 存储消息)
      • [(5) 消费消息](#(5) 消费消息)
      • [(6) 删除消息](#(6) 删除消息)
  • [三. RabbitMQ的四种交换机类型](#三. RabbitMQ的四种交换机类型)
    • [1. Fanout类型](#1. Fanout类型)
    • [2. Direct](#2. Direct)
    • [3. Topic](#3. Topic)
    • [4. Headers](#4. Headers)
  • [四. RabbitMQ的七种工作模式](#四. RabbitMQ的七种工作模式)
    • [1. Simple模式](#1. Simple模式)
      • [(1) 解释](#(1) 解释)
      • [(2) 生产者代码](#(2) 生产者代码)
      • [(3) 消费者代码](#(3) 消费者代码)
      • [(4) 公共代码](#(4) 公共代码)
      • [(5) 效果图](#(5) 效果图)
    • [2. Work Queue(工作队列模式/竞争消费模式)](#2. Work Queue(工作队列模式/竞争消费模式))
      • [(1) 解释](#(1) 解释)
      • [(2) 生产者代码](#(2) 生产者代码)
      • [(3) 消费者1代码](#(3) 消费者1代码)
      • [(4) 消费者2代码](#(4) 消费者2代码)
      • [(5) 公共代码](#(5) 公共代码)
      • [(6) 效果图](#(6) 效果图)
    • [3. Publish/Subscribe(发布/订阅)](#3. Publish/Subscribe(发布/订阅))
      • [(1) 解释](#(1) 解释)
      • [(2) 生产者代码](#(2) 生产者代码)
      • [(3) 消费者1代码](#(3) 消费者1代码)
      • [(4) 消费者2代码](#(4) 消费者2代码)
      • [(5) 公共代码](#(5) 公共代码)
      • [(6) 效果图](#(6) 效果图)
    • [4. Routing(路由模式)](#4. Routing(路由模式))
      • [(1) 解释](#(1) 解释)
      • [(2) 生产者代码](#(2) 生产者代码)
      • [(3) 消费者1代码](#(3) 消费者1代码)
      • [(4) 消费者2代码](#(4) 消费者2代码)
      • [(5) 公共代码](#(5) 公共代码)
      • [(6) 效果图](#(6) 效果图)
    • [5. Topics(通配符模式)](#5. Topics(通配符模式))
      • [(1) 解释](#(1) 解释)
      • [(2) 生产者代码](#(2) 生产者代码)
      • [(3) 消费者1代码](#(3) 消费者1代码)
      • [(4) 消费者2代码](#(4) 消费者2代码)
      • [(5) 公共代码](#(5) 公共代码)
      • [(6) 效果图](#(6) 效果图)
    • [6. RPC(RPC通信)](#6. RPC(RPC通信))
      • [(1) 解释](#(1) 解释)
      • [(2) 客户端代码](#(2) 客户端代码)
      • [(3) 服务器代码](#(3) 服务器代码)
      • [(4) 公共代码](#(4) 公共代码)
      • [(5) 效果图](#(5) 效果图)
    • [7. Publisher Confirms(发布确认)](#7. Publisher Confirms(发布确认))
      • [(1) 解释](#(1) 解释)
      • [(2) 生产者代码](#(2) 生产者代码)
      • [(3) 消费者代码](#(3) 消费者代码)

一. MQ的应用场景

1. 异步解耦

业务中一些操作非常耗时, 但是又不需要立即得到响应时, 可以借助中间件mq, 在不影响主线程的情况下, 利用消息队列进行通信, 典型场景就是在注册场景中, 就爱那个用户信息插入到数据库中后, 通常会给用户发送一个邮件来表达欢迎, 这时发送邮件消息就可以交给MQ来在后台进行, 不会影响主业务的运行

2. 流量削峰

在访问量突增的场景中, 服务器仍想要维持正常运行, 这时可以将请求存入到消息队列, 按照系统能处理的力度来逐渐将这些请求消化

3. 消息订阅分发

当多个系统都依赖同一数据变化来进行响应时, 可以使用MQ来进行消息数据的分发, 即一个系统将该数据改变时, 会将消息发给MQ, 其他需要作出响应的系统通过订阅该消息, 从而拉取数据, 这就不需要多次轮询数据库了

4. 延迟通知

需要在特定时间后才发送通知的场景中, 可以使用MQ的延迟消息功能, 典型场景就是下单后30分钟内未支付, 会将未支付的消息分发给订单系统来自动取消订单

二. RabbitMQ的结构分析与工作流程

1. AMQP协议

RabbitMQ基于AMQP协议进行通信的, AMQP是一种高级消息队列协议, 将交换机和队列等组件组织了起来, 进行接收和发送消息

2. 客户端与服务器

Broker作为RabbitMQ的服务器, 负责接收和发送消息, 分别有生产者(Producer)和消费者(Consumer )两种客户端, 生产者负责生产消息给消息队列, 消费者通过消息队列来进行消费

3. Connection

网络连接, 它允许客户端与 RabbitMQ通信, 是客户端和RabbitMQ服务器的一个TCP连接, 这个连接是通信的基础, 负责传输所有的数据

4. Channel

通道, 基于Connection的一个抽象层, 一个Connection(TCP连接)可以有多个通道, 主要是将消息的读写操作复用到一个TCP连接上, Channel既可以接收处理消息也可以同时发送消息(通常的用法一些Channel专门负责接收, 一些Channel专门负责发送), 减少了建立与销毁TCP的开销, 每个Channel都是独立的虚拟连接, 消息的接受与发送都要通过Channel

5. Virtual host

虚拟主机, 为消息队列提供了逻辑上的虚拟隔离, 一个BrokerServer(RabbitMQ服务器)上可以有多个Virtual host, 不同的用户使用同一个RabbitMQ服务时, 可以划分多个虚拟机来作区分, 每个虚拟机又有自己的交换机和队列
看到这里你是不是感觉很熟悉, 对的, 和我们学过的MySQL上的database(数据库)的概念类似, 一个服务可以有多个数据库, 每个数据库又有单独的表, 库与库之间相互独立

6. Exchange

交换机, 存在于虚拟机当中. 负责接收生产者发送的信息, 并根据自身的路由算法和规则, 将消息路由到对应的一或多个Queue(队列)中, 如果没有相对应的队列会根据消息的mandatory 标志来决定销毁(false)还是返还(true)

7. Queue

队列, 是RabbitMQ的内部对象, 消息的实际存储者, 多个消费者可以订阅同一个队列, 当然一个消费者也可以订阅多个队列

8. 工作流程

(1) 创建连接

生产者(Producer)与RabbitMQ服务器(Broker)建立TCP连接(Connection), 开辟通道(Channel)

(2) 声明交换机和队列

Producer声明一个交换机(Exchange), 并将队列(Queue)绑定到交换机上

(3) 发送消息

生产者将消息发送给Broker

(4) 存储消息

Broker接收到消息后, 通过交换机存入到对应的Queue中, 如果没有找到队列, 会根据Headers标志(上面有生产者预留的配置), 来确定消息的丢弃或者返还

(5) 消费消息

消费者订阅了队列之后, 当有新消息加入时, 会从Queue中获取消息来处理, 并向RabbitMQ发送消息确认收到

(6) 删除消息

消息被确认或, RabbitMQ会将该消息从队列中删除


三. RabbitMQ的四种交换机类型

1. 前面我们了解到, 交换机(Exchange)会根据自身的路由策略来将消息转发到相应的队列(Queue)中, 这里我们来讲解RabbitMQ实现的四种类型的交换机, 不同类型意味着路由策略不同
2. 而在AMQP中共有6种不同类型的交换机, 但因为System和自定义类型的交换机, 在RabbitMQ没有实现, 这里不会解释
3. 在生产者发送消息时, 会与交换机发送一个RoutingKey, 用来告诉交换机该如何处理, 在RabbitMQ中, 交换机与队列之间BindingKey联系起来, 这里BindingKey其实属于是RoutingKey的另一种类型

1. Fanout类型

广播类型的交换机, 会将收到的消息转发到所有与之绑定的队列上

2. Direct

定向类型的交换机, 这里交换机会将消息发送给RoutingKey与BindingKey相同的队列

3. Topic

通配符匹配类型的队列, 会把消息发给符合Routing Pattern(匹配规则)的队列

4. Headers

不一排路由键匹配规则, 通过消息中的Headers属性进行匹配队列, 但性能很差, 基本很少见到

四. RabbitMQ的七种工作模式

① RabbitMQ的工作模式与交换机类型息息相关, 可以说有的交换机类型直接就是一种工作模式
② 这里在前两种模式中, 交换机只起到转发作用, 没有起到路由作用, 为了和其他工作模式作区分就没有画出, 图片摘自官方

1. Simple模式

(1) 解释

简单模式, 一个生产者, 一个消息队列, 一个消费者, 一个消息也只能被消费一次, 这也被称为点对点模式(Point-to-Point)


(2) 生产者代码

① 通过上面RabbitMQ的工作流程, 来一步步写出代码, 要注意, 我们这里使用的是RabbitMQ内置的交换机, 并没有声明新的交换机
② 内置交换机的名称为空字符, 特性就是会将消息路由到RoutingKey和Queue名称一致的队列上
③ 关闭资源时需要注意, 因为Channel是基于Connection的, 所以要先关闭Channel再关闭Connection资源, 如果顺序颠倒会报连接不存在的错误

java 复制代码
package com.ran.simple;

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

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.TimeoutException;

/**
 * Created with IntelliJ IDEA.
 * Description:
 * User: ran
 * Date: 2026-03-19
 * Time: 18:50
 */
public class Producer {
    public static void main(String[] args) throws IOException, TimeoutException {
        // 1. 建立TCP连接, 这里使用连接工厂来配置参数
        /*
        * 可以借鉴我们之前连接MySQL的流程
        * 建立连接的参数:
        * 1. 主机ip
        * 2. 端口号
        * 3. 账号密码
        * 4. 虚拟机(数据库)
        * */
        ConnectionFactory connectionFactory = new ConnectionFactory();
        connectionFactory.setHost(Constants.HOST);
        connectionFactory.setPort(Constants.PORT); // RabbitMQ服务的默认端口
        connectionFactory.setUsername(Constants.USER_NAME);
        connectionFactory.setPassword(Constants.PASSWORD);
        connectionFactory.setVirtualHost(Constants.VIRTUAL_HOST);
        Connection connection = connectionFactory.newConnection();// new一个连接对象
        // 2. 开启通道
        Channel channel = connection.createChannel();
        // 3. 声明交换机, 这里使用默认交换机
        // 4. 声明队列
        /*
        * Queue.DeclareOk queueDeclare(String queue, boolean durable, boolean exclusive, boolean autoDelete,
                                 Map<String, Object> arguments)
        * 参数:
        * queue: 队列的名字
        * durable: 是否开启持久化,即写到硬盘上
        * exclusive: 是否由一个消费者独占这个队列. 即只能被一个消费者消费
        * autoDelete: 如果没有消费者, 是否自动删除消息
        * arguments: 其他配置, 如队列的容量
        */
        channel.queueDeclare(Constants.SIMPLE_QUEUE,true,false,false,null);
        // 5. 发送消息
        /*
        * void basicPublish(String exchange, String routingKey, BasicProperties props, byte[] body)
        * 参数:
        * exchange: 交换机的名字, 内置交换机用空字符串
        * routingKey: 路由的key,即要路由到哪个队列中 (因为我们使用的是默认内置的交换机, 所以和Queue名称保持一致即可)
        * props: 其它属性配置
        * body: 消息主体
        */
        for (int i = 0; i < 10; i++) {
            String mag = "hello, 我是景画 - " + i;
            channel.basicPublish("", Constants.SIMPLE_QUEUE, null, mag.getBytes(StandardCharsets.UTF_8));
        }
        System.out.println("发送消息成功");
        // 6. 资源释放
        channel.close();
        connection.close();
        
    }
}

(3) 消费者代码

java 复制代码
package com.ran.simple;

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

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

/**
 * Created with IntelliJ IDEA.
 * Description:
 * User: ran
 * Date: 2026-03-19
 * Time: 20:39
 */
public class Consumer {
    public static void main(String[] args) throws IOException, TimeoutException {
        // 1. 建立连接
        // 1. 建立TCP连接, 这里使用连接工厂来配置参数
        /*
         * 可以借鉴我们之前连接MySQL的流程
         * 建立连接的参数:
         * 1. 主机ip
         * 2. 端口号
         * 3. 账号密码
         * 4. 虚拟机(数据库)
         * */
        ConnectionFactory connectionFactory = new ConnectionFactory();
        connectionFactory.setHost(Constants.HOST);
        connectionFactory.setPort(Constants.PORT); // RabbitMQ服务的默认端口
        connectionFactory.setUsername(Constants.USER_NAME);
        connectionFactory.setPassword(Constants.PASSWORD);
        connectionFactory.setVirtualHost(Constants.VIRTUAL_HOST);
        Connection connection = connectionFactory.newConnection();// new一个连接对象
        // 2. 开启通道
        Channel channel = connection.createChannel();
        // 3. 声明队列
        channel.queueDeclare(Constants.SIMPLE_QUEUE,true,false,false,null);
        // 4. 接收消息
        DefaultConsumer consumer = new DefaultConsumer(channel){
            // 重写的该方法就是收到消息后要执行的逻辑
            /*
             * consumerTag: 消费者标签, 消费者订阅队列时指定的
             * envelope: 封包信息, 消息对应的交换机和队列名称等信息
             * properties: 一些配置信息
             * body: 消息主体
             */
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                System.out.println("消费者标签: " + consumerTag);
                System.out.println("封包信息: " + envelope);
                System.out.println("收到的消息: " + new String(body));
            }
        };
        /*
         * String basicConsume(String queue, boolean autoAck, Consumer callback)
         * 参数:
         * queue: 队列名称
         * autoAck: 收到消息后是否自动确认
         * callback: 收到消息后需要执行的逻辑, Consumer是接口
         */
        channel.basicConsume(Constants.SIMPLE_QUEUE,true,consumer);
        // 5. 关闭资源
        channel.close();
        connection.close();
    }
}

(4) 公共代码

java 复制代码
package com.ran.constant;

/**
 * Created with IntelliJ IDEA.
 * Description:
 * User: ran
 * Date: 2026-03-22
 * Time: 18:47
 */
public class Constants {
    public static final String HOST = "localhost";
    public static final int PORT = 5672;
    public static final String USER_NAME = "admin";
    public static final String PASSWORD = "admin";
    public static final String VIRTUAL_HOST = "ran";
    public static final String SIMPLE_QUEUE = "hello";
}

(5) 效果图


2. Work Queue(工作队列模式/竞争消费模式)

(1) 解释

1. 一个生产者, 多个消费者, 队列中的消息会被多个消费者竞争消费, 这就意味着同一消息不会分配给不同队列, 每个队列中的消息都不同
2. 这里需要先启动消费者代码再启动生产者代码, 因为有多个消费者, 有消息时先启动任意一个消费者都会导致消息分配不均

适用场景: 集群中做异步处理, 典型例子是订票时, 订单系统会将多个订票消息发送给RabbitMQ服务器, 短信服务就通过从RabbitMQ获取信息来进行处理, 这里会有多个短信服务, 每个短信服务负责处理一批信息 (订票短信肯定也不需要重复发送吧)


(2) 生产者代码

java 复制代码
package com.ran.work;

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

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.TimeoutException;

/**
 * Created with IntelliJ IDEA.
 * Description:
 * User: ran
 * Date: 2026-03-19
 * Time: 18:50
 */
public class Producer {
    public static void main(String[] args) throws IOException, TimeoutException {
        // 1. 建立TCP连接, 这里使用连接工厂来配置参数
        /*
        * 可以借鉴我们之前连接MySQL的流程
        * 建立连接的参数:
        * 1. 主机ip
        * 2. 端口号
        * 3. 账号密码
        * 4. 虚拟机(数据库)
        * */
        ConnectionFactory connectionFactory = new ConnectionFactory();
        connectionFactory.setHost(Constants.HOST);
        connectionFactory.setPort(Constants.PORT); // RabbitMQ服务的默认端口
        connectionFactory.setUsername(Constants.USER_NAME);
        connectionFactory.setPassword(Constants.PASSWORD);
        connectionFactory.setVirtualHost(Constants.VIRTUAL_HOST);
        Connection connection = connectionFactory.newConnection();// new一个连接对象
        // 2. 开启通道
        Channel channel = connection.createChannel();
        // 3. 声明交换机, 这里使用默认交换机
        // 4. 声明队列
        channel.queueDeclare(Constants.WORK_QUEUE,true,false,false,null);
        // 5. 发送消息
        for (int i = 0; i < 10; i++) {
            String mag = "hello, 我是景画 - " + i;
            channel.basicPublish("", Constants.WORK_QUEUE, null, mag.getBytes(StandardCharsets.UTF_8));
        }
        System.out.println("发送消息成功");
        // 6. 资源释放
        channel.close();
        connection.close();
        
    }
}

(3) 消费者1代码

java 复制代码
package com.ran.work;

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

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

/**
 * Created with IntelliJ IDEA.
 * Description:
 * User: ran
 * Date: 2026-03-19
 * Time: 20:39
 */
public class Consumer1 {
    public static void main(String[] args) throws IOException, TimeoutException {
        // 1. 建立连接
        // 1. 建立TCP连接, 这里使用连接工厂来配置参数
        /*
         * 可以借鉴我们之前连接MySQL的流程
         * 建立连接的参数:
         * 1. 主机ip
         * 2. 端口号
         * 3. 账号密码
         * 4. 虚拟机(数据库)
         * */
        ConnectionFactory connectionFactory = new ConnectionFactory();
        connectionFactory.setHost(Constants.HOST);
        connectionFactory.setPort(Constants.PORT); // RabbitMQ服务的默认端口
        connectionFactory.setUsername(Constants.USER_NAME);
        connectionFactory.setPassword(Constants.PASSWORD);
        connectionFactory.setVirtualHost(Constants.VIRTUAL_HOST);
        Connection connection = connectionFactory.newConnection();// new一个连接对象
        // 2. 开启通道
        Channel channel = connection.createChannel();
        // 3. 声明队列
        channel.queueDeclare(Constants.WORK_QUEUE,true,false,false,null);
        // 4. 接收消息
        DefaultConsumer consumer = new DefaultConsumer(channel){
            // 重写的该方法就是收到消息后要执行的逻辑
            /*
             * consumerTag: 消费者标签, 消费者订阅队列时指定的
             * envelope: 封包信息, 消息对应的交换机和队列名称等信息
             * properties: 一些配置信息
             * body: 消息主体
             */
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                System.out.println("消费者标签: " + consumerTag);
                System.out.println("封包信息: " + envelope);
                System.out.println("收到的消息: " + new String(body));
            }
        };
        /*
         * String basicConsume(String queue, boolean autoAck, Consumer callback)
         * 参数:
         * queue: 队列名称
         * autoAck: 收到消息后是否自动确认
         * callback: 收到消息后需要执行的逻辑, Consumer是接口
         */
        channel.basicConsume(Constants.WORK_QUEUE,true,consumer);
        // 5. 关闭资源
//        channel.close();
//        connection.close();
    }
}

(4) 消费者2代码

java 复制代码
package com.ran.work;

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

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

/**
 * Created with IntelliJ IDEA.
 * Description:
 * User: ran
 * Date: 2026-03-19
 * Time: 20:39
 */
public class Consumer2 {
    public static void main(String[] args) throws IOException, TimeoutException {
        // 1. 建立连接
        // 1. 建立TCP连接, 这里使用连接工厂来配置参数
        /*
         * 可以借鉴我们之前连接MySQL的流程
         * 建立连接的参数:
         * 1. 主机ip
         * 2. 端口号
         * 3. 账号密码
         * 4. 虚拟机(数据库)
         * */
        ConnectionFactory connectionFactory = new ConnectionFactory();
        connectionFactory.setHost(Constants.HOST);
        connectionFactory.setPort(Constants.PORT); // RabbitMQ服务的默认端口
        connectionFactory.setUsername(Constants.USER_NAME);
        connectionFactory.setPassword(Constants.PASSWORD);
        connectionFactory.setVirtualHost(Constants.VIRTUAL_HOST);
        Connection connection = connectionFactory.newConnection();// new一个连接对象
        // 2. 开启通道
        Channel channel = connection.createChannel();
        // 3. 声明队列
        channel.queueDeclare(Constants.WORK_QUEUE,true,false,false,null);
        // 4. 接收消息
        DefaultConsumer consumer = new DefaultConsumer(channel){
            // 重写的该方法就是收到消息后要执行的逻辑
            /*
             * consumerTag: 消费者标签, 消费者订阅队列时指定的
             * envelope: 封包信息, 消息对应的交换机和队列名称等信息
             * properties: 一些配置信息
             * body: 消息主体
             */
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                System.out.println("消费者标签: " + consumerTag);
                System.out.println("封包信息: " + envelope);
                System.out.println("收到的消息: " + new String(body));
            }
        };
        /*
         * String basicConsume(String queue, boolean autoAck, Consumer callback)
         * 参数:
         * queue: 队列名称
         * autoAck: 收到消息后是否自动确认
         * callback: 收到消息后需要执行的逻辑, Consumer是接口
         */
        channel.basicConsume(Constants.WORK_QUEUE,true,consumer);
        // 5. 关闭资源
//        channel.close();
//        connection.close();
    }
}

(5) 公共代码

java 复制代码
package com.ran.constant;

/**
 * Created with IntelliJ IDEA.
 * Description:
 * User: ran
 * Date: 2026-03-22
 * Time: 18:47
 */
public class Constants {
    public static final String HOST = "localhost";
    public static final int PORT = 5672;
    public static final String USER_NAME = "admin";
    public static final String PASSWORD = "admin";
    public static final String VIRTUAL_HOST = "ran";
    public static final String SIMPLE_QUEUE = "hello";
    public static final String WORK_QUEUE = "work.queue";
}

(6) 效果图

消费者1

消费者2


3. Publish/Subscribe(发布/订阅)

(1) 解释

① 发布订阅模式, 交换机会将消息复制多份, 无条件来转发给所有与之绑定的队列, 来确保每一个消费者(订阅者)都能收到同一份消息, 是不是和Fanout类型交换机的描述很类似!
② 典型应用场景是气象局发布天气预报消息到交换机, 各大门户网站通过将自建的队列与该交换机绑定后, 来自动获取天气信息

(2) 生产者代码

这里我们不再使用内置交换机, 而是自己声明了一个Fanout类型的交换机, 并声明了两个队列, 两个队列与交换机绑定时的routingkey参数, 就是BindingKey, 这里为空字符串, 作用是交换机把所有消息都转发到队列上

java 复制代码
package com.ran.fanout;

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

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.TimeoutException;

/**
 * Created with IntelliJ IDEA.
 * Description:
 * User: ran
 * Date: 2026-03-22
 * Time: 22:26
 */
public class Producer {
    public static void main(String[] args) throws IOException, TimeoutException {
        // 1. 建立TCP连接, 这里使用连接工厂来配置参数
        /*
         * 可以借鉴我们之前连接MySQL的流程
         * 建立连接的参数:
         * 1. 主机ip
         * 2. 端口号
         * 3. 账号密码
         * 4. 虚拟机(数据库)
         * */
        ConnectionFactory connectionFactory = new ConnectionFactory();
        connectionFactory.setHost(Constants.HOST);
        connectionFactory.setPort(Constants.PORT); // RabbitMQ服务的默认端口
        connectionFactory.setUsername(Constants.USER_NAME);
        connectionFactory.setPassword(Constants.PASSWORD);
        connectionFactory.setVirtualHost(Constants.VIRTUAL_HOST);
        Connection connection = connectionFactory.newConnection();// new一个连接对象
        // 2. 开启通道
        Channel channel = connection.createChannel();
        // 3. 声明交换机
        /*
        * Exchange.DeclareOk exchangeDeclare(String exchange,BuiltinExchangeType type, boolean durable, boolean autoDelete, boolean internal, Map<String, Object> arguments) throws IOException;
        *
        * 参数介绍:
        * 1. exchange: 交换机的名称
        * 2. type: 交换机的类型, 这里是枚举
        * 3. durable: 是否持久化
        * 4. autoDelete: 是否在不再被使用时自动删除, 不被使用指的是没有队列与之绑定
        * 5. internal: 是否开启内部链接使用, 开启的话客户端就连接不上
        * 6. arguments: 其他高级参数设置
        */
        // 这里我们使用只有三个参数的方法
        channel.exchangeDeclare(Constants.FANOUT_EXCHANGE, BuiltinExchangeType.FANOUT, true);
        // 4. 声明两个队列
        channel.queueDeclare(Constants.FANOUT_QUEUE1, true, false, false, null);
        channel.queueDeclare(Constants.FANOUT_QUEUE2, true, false, false, null);
        // 5. 绑定交换机
        // BindingKey为 "" , 代表交换机任何消息都发送到队列上
        channel.queueBind(Constants.FANOUT_QUEUE1, Constants.FANOUT_EXCHANGE, "");
        channel.queueBind(Constants.FANOUT_QUEUE2, Constants.FANOUT_EXCHANGE, "");
        // 6. 发送消息, routingkey为 "" , 所有消息都转发
        String msg = "hello, 发布订阅模式测试~";
        channel.basicPublish(Constants.FANOUT_EXCHANGE, "",null, msg.getBytes(StandardCharsets.UTF_8));
        System.out.println("消息发送成功");
        // 7. 释放资源
        channel.close();
        connection.close();
    }
}

(3) 消费者1代码

java 复制代码
package com.ran.fanout;

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

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

/**
 * Created with IntelliJ IDEA.
 * Description:
 * User: ran
 * Date: 2026-03-19
 * Time: 20:39
 */
public class Consumer1 {
    public static void main(String[] args) throws IOException, TimeoutException {
        // 1. 建立连接
        // 1. 建立TCP连接, 这里使用连接工厂来配置参数
        /*
         * 可以借鉴我们之前连接MySQL的流程
         * 建立连接的参数:
         * 1. 主机ip
         * 2. 端口号
         * 3. 账号密码
         * 4. 虚拟机(数据库)
         * */
        ConnectionFactory connectionFactory = new ConnectionFactory();
        connectionFactory.setHost(Constants.HOST);
        connectionFactory.setPort(Constants.PORT); // RabbitMQ服务的默认端口
        connectionFactory.setUsername(Constants.USER_NAME);
        connectionFactory.setPassword(Constants.PASSWORD);
        connectionFactory.setVirtualHost(Constants.VIRTUAL_HOST);
        Connection connection = connectionFactory.newConnection();// new一个连接对象
        // 2. 开启通道
        Channel channel = connection.createChannel();
        // 3. 声明队列
        channel.queueDeclare(Constants.FANOUT_QUEUE1,true,false,false,null);
        // 4. 接收消息
        DefaultConsumer 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);
                System.out.println("收到的消息: " + new String(body));
            }
        };
        channel.basicConsume(Constants.FANOUT_QUEUE1,true,consumer);
    }
}

(4) 消费者2代码

java 复制代码
package com.ran.fanout;

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

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

/**
 * Created with IntelliJ IDEA.
 * Description:
 * User: ran
 * Date: 2026-03-19
 * Time: 20:39
 */
public class Consumer2 {
    public static void main(String[] args) throws IOException, TimeoutException {
        // 1. 建立连接
        // 1. 建立TCP连接, 这里使用连接工厂来配置参数
        /*
         * 可以借鉴我们之前连接MySQL的流程
         * 建立连接的参数:
         * 1. 主机ip
         * 2. 端口号
         * 3. 账号密码
         * 4. 虚拟机(数据库)
         * */
        ConnectionFactory connectionFactory = new ConnectionFactory();
        connectionFactory.setHost(Constants.HOST);
        connectionFactory.setPort(Constants.PORT); // RabbitMQ服务的默认端口
        connectionFactory.setUsername(Constants.USER_NAME);
        connectionFactory.setPassword(Constants.PASSWORD);
        connectionFactory.setVirtualHost(Constants.VIRTUAL_HOST);
        Connection connection = connectionFactory.newConnection();// new一个连接对象
        // 2. 开启通道
        Channel channel = connection.createChannel();
        // 3. 声明队列
        channel.queueDeclare(Constants.FANOUT_QUEUE2,true,false,false,null);
        // 4. 接收消息
        DefaultConsumer 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);
                System.out.println("收到的消息: " + new String(body));
            }
        };
        channel.basicConsume(Constants.FANOUT_QUEUE2,true,consumer);
    }
}

(5) 公共代码

java 复制代码
package com.ran.constant;

/**
 * Created with IntelliJ IDEA.
 * Description:
 * User: ran
 * Date: 2026-03-22
 * Time: 18:47
 */
public class Constants {
    public static final String HOST = "localhost";
    public static final int PORT = 5672;
    public static final String USER_NAME = "admin";
    public static final String PASSWORD = "admin";
    public static final String VIRTUAL_HOST = "ran";

    public static final String SIMPLE_QUEUE = "hello"; // 普通模式-队列

    public static final String WORK_QUEUE = "work.queue"; // 工作模式-队列

    public static final String FANOUT_EXCHANGE = "fanout.exchange"; // 广播/订阅模式-交换机

    public static final String FANOUT_QUEUE1 = "fanout.queue1"; // 广播/订阅模式-队列1
    public static final String FANOUT_QUEUE2 = "fanout.queue2"; // 广播/订阅模式-队列2

}

(6) 效果图

可以看出, 我们只发送了一次消息, 但是交换机路由到了两个队列中

消费者1

消费者2

4. Routing(路由模式)

(1) 解释

① 发布订阅模式的一个变种, 交换机根据RoutingKey的规则, 来发送给与BindingKey匹配的队列, 消费者再获取信息
② 应用场景: 系统日志的打印, 日志等级分为error, warning, info,debug, 通过路由到不同队列, 来输出到不同的日志文件

(2) 生产者代码

这里交换机类型变为了Direct, 只有当BindingKey和RoutingKey完全匹配时, 交换机才会转发消息到队列上

java 复制代码
package com.ran.direct;

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

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.TimeoutException;

/**
 * Created with IntelliJ IDEA.
 * Description:
 * User: ran
 * Date: 2026-03-22
 * Time: 22:26
 */
public class Producer {
    public static void main(String[] args) throws IOException, TimeoutException {
        // 1. 建立TCP连接, 这里使用连接工厂来配置参数
        /*
         * 可以借鉴我们之前连接MySQL的流程
         * 建立连接的参数:
         * 1. 主机ip
         * 2. 端口号
         * 3. 账号密码
         * 4. 虚拟机(数据库)
         * */
        ConnectionFactory connectionFactory = new ConnectionFactory();
        connectionFactory.setHost(Constants.HOST);
        connectionFactory.setPort(Constants.PORT); // RabbitMQ服务的默认端口
        connectionFactory.setUsername(Constants.USER_NAME);
        connectionFactory.setPassword(Constants.PASSWORD);
        connectionFactory.setVirtualHost(Constants.VIRTUAL_HOST);
        Connection connection = connectionFactory.newConnection();// new一个连接对象
        // 2. 开启通道
        Channel channel = connection.createChannel();
        // 3. 声明交换机
        /*
        * Exchange.DeclareOk exchangeDeclare(String exchange,BuiltinExchangeType type, boolean durable, boolean autoDelete, boolean internal, Map<String, Object> arguments) throws IOException;
        *
        * 参数介绍:
        * 1. exchange: 交换机的名称
        * 2. type: 交换机的类型, 这里是枚举
        * 3. durable: 是否持久化
        * 4. autoDelete: 是否在不再被使用时自动删除, 不被使用指的是没有队列与之绑定
        * 5. internal: 是否开启内部链接使用, 开启的话客户端就连接不上
        * 6. arguments: 其他高级参数设置
        */
        // 这里我们使用只有三个参数的方法
        channel.exchangeDeclare(Constants.DIRECT_EXCHANGE, BuiltinExchangeType.DIRECT, true);
        // 4. 声明两个队列
        channel.queueDeclare(Constants.DIRECT_QUEUE1, true, false, false, null);
        channel.queueDeclare(Constants.DIRECT_QUEUE2, true, false, false, null);
        // 5. 绑定交换机
        // BindingKey为 "" , 代表交换机任何消息都发送到队列上
        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 = "hello, 我的routingkey为 a";
        channel.basicPublish(Constants.DIRECT_EXCHANGE, "a",null, msg.getBytes(StandardCharsets.UTF_8));


        String msg_b = "hello, 我的routingkey为 b";
        channel.basicPublish(Constants.DIRECT_EXCHANGE, "b",null, msg_b.getBytes(StandardCharsets.UTF_8));

        String msg_c = "hello, 我的routingkey为 c";
        channel.basicPublish(Constants.DIRECT_EXCHANGE, "c",null, msg_c.getBytes(StandardCharsets.UTF_8));

        System.out.println("消息发送成功");
        // 7. 释放资源
        channel.close();
        connection.close();
    }
}

(3) 消费者1代码

java 复制代码
package com.ran.direct;

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

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

/**
 * Created with IntelliJ IDEA.
 * Description:
 * User: ran
 * Date: 2026-03-19
 * Time: 20:39
 */
public class Consumer1 {
    public static void main(String[] args) throws IOException, TimeoutException {
        // 1. 建立连接
        // 1. 建立TCP连接, 这里使用连接工厂来配置参数
        ConnectionFactory connectionFactory = new ConnectionFactory();
        connectionFactory.setHost(Constants.HOST);
        connectionFactory.setPort(Constants.PORT); // RabbitMQ服务的默认端口
        connectionFactory.setUsername(Constants.USER_NAME);
        connectionFactory.setPassword(Constants.PASSWORD);
        connectionFactory.setVirtualHost(Constants.VIRTUAL_HOST);
        Connection connection = connectionFactory.newConnection();// new一个连接对象
        // 2. 开启通道
        Channel channel = connection.createChannel();
        // 3. 声明队列
        channel.queueDeclare(Constants.DIRECT_QUEUE1,true,false,false,null);
        // 4. 接收消息
        DefaultConsumer 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);
                System.out.println("收到的消息: " + new String(body));
            }
        };
        channel.basicConsume(Constants.DIRECT_QUEUE1,true,consumer);
    }
}

(4) 消费者2代码

java 复制代码
package com.ran.direct;

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

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

/**
 * Created with IntelliJ IDEA.
 * Description:
 * User: ran
 * Date: 2026-03-19
 * Time: 20:39
 */
public class Consumer2 {
    public static void main(String[] args) throws IOException, TimeoutException {
        // 1. 建立连接
        // 1. 建立TCP连接, 这里使用连接工厂来配置参数
        /*
         * 可以借鉴我们之前连接MySQL的流程
         * 建立连接的参数:
         * 1. 主机ip
         * 2. 端口号
         * 3. 账号密码
         * 4. 虚拟机(数据库)
         * */
        ConnectionFactory connectionFactory = new ConnectionFactory();
        connectionFactory.setHost(Constants.HOST);
        connectionFactory.setPort(Constants.PORT); // RabbitMQ服务的默认端口
        connectionFactory.setUsername(Constants.USER_NAME);
        connectionFactory.setPassword(Constants.PASSWORD);
        connectionFactory.setVirtualHost(Constants.VIRTUAL_HOST);
        Connection connection = connectionFactory.newConnection();// new一个连接对象
        // 2. 开启通道
        Channel channel = connection.createChannel();
        // 3. 声明队列
        channel.queueDeclare(Constants.DIRECT_QUEUE2,true,false,false,null);
        // 4. 接收消息
        DefaultConsumer 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(Constants.DIRECT_QUEUE2,true,consumer);
    }
}

(5) 公共代码

java 复制代码
package com.ran.constant;

/**
 * Created with IntelliJ IDEA.
 * Description:
 * User: ran
 * Date: 2026-03-22
 * Time: 18:47
 */
public class Constants {
    public static final String HOST = "localhost";
    public static final int PORT = 5672;
    public static final String USER_NAME = "admin";
    public static final String PASSWORD = "admin";
    public static final String VIRTUAL_HOST = "ran";

    public static final String SIMPLE_QUEUE = "hello"; // 普通模式-队列

    public static final String WORK_QUEUE = "work.queue"; // 工作模式-队列

    public static final String FANOUT_EXCHANGE = "fanout.exchange"; // 广播/订阅模式-交换机

    public static final String FANOUT_QUEUE1 = "fanout.queue1"; // 广播/订阅模式-队列1
    public static final String FANOUT_QUEUE2 = "fanout.queue2"; // 广播/订阅模式-队列2

    public static final String DIRECT_EXCHANGE = "direct.exchange"; // 路由模式-交换机
    public static final String DIRECT_QUEUE1 = "direct.queue1"; // 路由模式-队列1
    public static final String DIRECT_QUEUE2 = "direct.queue2"; // 路由模式-队列2

}

(6) 效果图

消费者1

消费者2

5. Topics(通配符模式)

(1) 解释

路由模式的进步版本, 在BindingKey基础上, 又先加了通配符匹配的功能, RoutingKey与BindingKey匹配更加灵活, 在RoutingKey/BindingKey中, 是通过 . 来区分一个个单词的

*(star) can substitute for exactly one word -> * 号可以匹配单个任意单词
# (hash) can substitute for zero or more words -> # 号可以匹配任意多个单词

(2) 生产者代码

交换机类型为topic

java 复制代码
package com.ran.topic;

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

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.TimeoutException;

/**
 * Created with IntelliJ IDEA.
 * Description:
 * User: ran
 * Date: 2026-03-22
 * Time: 22:26
 */
public class Producer {
    public static void main(String[] args) throws IOException, TimeoutException {
        // 1. 建立TCP连接, 这里使用连接工厂来配置参数
        ConnectionFactory connectionFactory = new ConnectionFactory();
        connectionFactory.setHost(Constants.HOST);
        connectionFactory.setPort(Constants.PORT); // RabbitMQ服务的默认端口
        connectionFactory.setUsername(Constants.USER_NAME);
        connectionFactory.setPassword(Constants.PASSWORD);
        connectionFactory.setVirtualHost(Constants.VIRTUAL_HOST);
        Connection connection = connectionFactory.newConnection();// new一个连接对象
        // 2. 开启通道
        Channel channel = connection.createChannel();
        // 3. 声明交换机
        // 这里我们使用只有三个参数的方法
        channel.exchangeDeclare(Constants.TOPIC_EXCHANGE, BuiltinExchangeType.TOPIC, true);
        // 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 = "hello, 我的routingkey为 jh.a.ran";
        channel.basicPublish(Constants.TOPIC_EXCHANGE, "jh.a.ran",null, msg.getBytes(StandardCharsets.UTF_8));

        String msg_b = "hello, 我的routingkey为 jh.aa.b";
        channel.basicPublish(Constants.TOPIC_EXCHANGE, "jh.aa.b",null, msg_b.getBytes(StandardCharsets.UTF_8));

        String msg_c = "hello, 我的routingkey为 c.jh";
        channel.basicPublish(Constants.TOPIC_EXCHANGE, "c.jh",null, msg_c.getBytes(StandardCharsets.UTF_8));

        System.out.println("消息发送成功");
        // 7. 释放资源
        channel.close();
        connection.close();
    }
}

(3) 消费者1代码

java 复制代码
package com.ran.topic;

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

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

/**
 * Created with IntelliJ IDEA.
 * Description:
 * User: ran
 * Date: 2026-03-19
 * Time: 20:39
 */
public class Consumer1 {
    public static void main(String[] args) throws IOException, TimeoutException {
        // 1. 建立连接
        // 1. 建立TCP连接, 这里使用连接工厂来配置参数
        ConnectionFactory connectionFactory = new ConnectionFactory();
        connectionFactory.setHost(Constants.HOST);
        connectionFactory.setPort(Constants.PORT); // RabbitMQ服务的默认端口
        connectionFactory.setUsername(Constants.USER_NAME);
        connectionFactory.setPassword(Constants.PASSWORD);
        connectionFactory.setVirtualHost(Constants.VIRTUAL_HOST);
        Connection connection = connectionFactory.newConnection();// new一个连接对象
        // 2. 开启通道
        Channel channel = connection.createChannel();
        // 3. 声明队列
        channel.queueDeclare(Constants.TOPIC_QUEUE1,true,false,false,null);
        // 4. 接收消息
        DefaultConsumer consumer = new DefaultConsumer(channel){
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                System.out.println("队列1收到的消息: " + new String(body));
            }
        };
        channel.basicConsume(Constants.TOPIC_QUEUE1,true,consumer);
    }
}

(4) 消费者2代码

java 复制代码
package com.ran.topic;

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

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

/**
 * Created with IntelliJ IDEA.
 * Description:
 * User: ran
 * Date: 2026-03-19
 * Time: 20:39
 */
public class Consumer2 {
    public static void main(String[] args) throws IOException, TimeoutException {
        // 1. 建立连接
        // 1. 建立TCP连接, 这里使用连接工厂来配置参数
        ConnectionFactory connectionFactory = new ConnectionFactory();
        connectionFactory.setHost(Constants.HOST);
        connectionFactory.setPort(Constants.PORT); // RabbitMQ服务的默认端口
        connectionFactory.setUsername(Constants.USER_NAME);
        connectionFactory.setPassword(Constants.PASSWORD);
        connectionFactory.setVirtualHost(Constants.VIRTUAL_HOST);
        Connection connection = connectionFactory.newConnection();// new一个连接对象
        // 2. 开启通道
        Channel channel = connection.createChannel();
        // 3. 声明队列
        channel.queueDeclare(Constants.TOPIC_QUEUE2,true,false,false,null);
        // 4. 接收消息
        DefaultConsumer consumer = new DefaultConsumer(channel){
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                System.out.println("队列2收到的消息: " + new String(body));
            }
        };
        channel.basicConsume(Constants.TOPIC_QUEUE2,true,consumer);
    }
}

(5) 公共代码

java 复制代码
package com.ran.constant;

/**
 * Created with IntelliJ IDEA.
 * Description:
 * User: ran
 * Date: 2026-03-22
 * Time: 18:47
 */
public class Constants {
    public static final String HOST = "localhost";
    public static final int PORT = 5672;
    public static final String USER_NAME = "admin";
    public static final String PASSWORD = "admin";
    public static final String VIRTUAL_HOST = "ran";

    public static final String SIMPLE_QUEUE = "hello"; // 普通模式-队列

    public static final String WORK_QUEUE = "work.queue"; // 工作模式-队列

    public static final String FANOUT_EXCHANGE = "fanout.exchange"; // 广播/订阅模式-交换机

    public static final String FANOUT_QUEUE1 = "fanout.queue1"; // 广播/订阅模式-队列1
    public static final String FANOUT_QUEUE2 = "fanout.queue2"; // 广播/订阅模式-队列2

    public static final String DIRECT_EXCHANGE = "direct.exchange"; // 路由模式-交换机
    public static final String DIRECT_QUEUE1 = "direct.queue1"; // 路由模式-队列1
    public static final String DIRECT_QUEUE2 = "direct.queue2"; // 路由模式-队列2

    public static final String TOPIC_EXCHANGE = "topic.exchange"; // 通配符模式-交换机
    public static final String TOPIC_QUEUE1 = "topic.queue1"; // 通配符模式-队列1
    public static final String TOPIC_QUEUE2 = "topic.queue2"; // 通配符模式-队列2

}

(6) 效果图

消费者1

消费者2

6. RPC(RPC通信)

(1) 解释

1. RPC模式没有生产者和消费者的概念, 而是变为了客户端和服务器, 这里请求消息有两个字段, replyTo用来指定队列, correlationId作用是用来标识是哪个请求消息
2. 客户端(Client)将消息发送到一个指定的存储请求消息的队列, 并且消息的属性中设置了replyTo字段, 指定了专属的响应队列, 用来接收服务器响应的消息
3. 服务器(Server)处理请求后, 将响应的消息发送到replyTo指定的队列, 并在响应消息中携带与请求相同的 correlationId, 确保客户端能正确关联
4. 客户端检查响应队列的状态, 当加入新响应时, 会先检查correlationId是否和之前请求相等


下面是最新的官方图片👇

(2) 客户端代码

java 复制代码
package com.ran.rpc;

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

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.UUID;
import java.util.concurrent.BlockingDeque;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.TimeoutException;

/**
 * Created with IntelliJ IDEA.
 * Description:
 * User: ran
 * Date: 2026-03-22
 * Time: 22:26
 */
public class RpcClient {
    public static void main(String[] args) throws IOException, TimeoutException, InterruptedException {
        // 1. 建立TCP连接, 这里使用连接工厂来配置参数
        ConnectionFactory connectionFactory = new ConnectionFactory();
        connectionFactory.setHost(Constants.HOST);
        connectionFactory.setPort(Constants.PORT); // RabbitMQ服务的默认端口
        connectionFactory.setUsername(Constants.USER_NAME);
        connectionFactory.setPassword(Constants.PASSWORD);
        connectionFactory.setVirtualHost(Constants.VIRTUAL_HOST);
        Connection connection = connectionFactory.newConnection();// new一个连接对象
        // 2. 开启通道
        Channel channel = connection.createChannel();
        // 3. 声明交换机, 使用默认交换机
        // 4. 声明队列
        channel.queueDeclare(Constants.RPC_REQUEST_QUEUE, true, false, false, null);
        channel.queueDeclare(Constants.RPC_RESPONSE_QUEUE, true, false, false, null);
        // 5. 发送消息
        String msg = "hello, rpc......";
        String correlationId = UUID.randomUUID().toString();
        AMQP.BasicProperties props = new AMQP.BasicProperties().builder()
                        .correlationId(correlationId)
                                .replyTo(Constants.RPC_RESPONSE_QUEUE)
                                        .build();
        channel.basicPublish("", Constants.RPC_REQUEST_QUEUE,props, msg.getBytes(StandardCharsets.UTF_8));
        System.out.println("请求发送成功");
        // 6. 接收响应
        // 用阻塞队列存储信息
        BlockingDeque<String> q = new LinkedBlockingDeque<>(1);
        DefaultConsumer consumer = new DefaultConsumer(channel){
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                String res = new String(body);
                System.out.println("接收到消息: " + res);
                if (properties.getCorrelationId().equals(correlationId)) {
                    // 校验correlationId一致
                    try {
                        q.put(res);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                }
            }
        };
        channel.basicConsume(Constants.RPC_RESPONSE_QUEUE,true,consumer);
        String res = q.take();
        System.out.println("RPC响应结果: " + res);
    }
}

(3) 服务器代码

java 复制代码
package com.ran.rpc;

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

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.UUID;
import java.util.concurrent.TimeoutException;

/**
 * Created with IntelliJ IDEA.
 * Description:
 * User: ran
 * Date: 2026-03-22
 * Time: 22:26
 */
public class RpcServer {
    public static void main(String[] args) throws IOException, TimeoutException, InterruptedException {
        // 1. 建立TCP连接, 这里使用连接工厂来配置参数
        ConnectionFactory connectionFactory = new ConnectionFactory();
        connectionFactory.setHost(Constants.HOST);
        connectionFactory.setPort(Constants.PORT); // RabbitMQ服务的默认端口
        connectionFactory.setUsername(Constants.USER_NAME);
        connectionFactory.setPassword(Constants.PASSWORD);
        connectionFactory.setVirtualHost(Constants.VIRTUAL_HOST);
        Connection connection = connectionFactory.newConnection();// new一个连接对象
        // 2. 开启通道
        Channel channel = connection.createChannel();
        // 3. 声明交换机, 使用默认交换机
        // 4. 声明队列
        channel.queueDeclare(Constants.RPC_REQUEST_QUEUE, true, false, false, null);
        channel.queueDeclare(Constants.RPC_RESPONSE_QUEUE, true, false, false, null);

        // 5. 接收请求
        // 用阻塞队列存储信息
        channel.basicQos(1);// 每次只接收一个请求, 防止请求过多混淆
        DefaultConsumer consumer = new DefaultConsumer(channel){
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                String request = new String(body);
                String res = "对[" + request + "]做出响应";
                // 6. 发送响应
                AMQP.BasicProperties props = new AMQP.BasicProperties().builder()
                        .correlationId(properties.getCorrelationId())
                        .build();
                channel.basicPublish("", Constants.RPC_RESPONSE_QUEUE,props, res.getBytes(StandardCharsets.UTF_8));
                System.out.println("响应发送成功");
                channel.basicAck(envelope.getDeliveryTag(), false);// 第一个参数deliveryTag为发送消息时自动删除的标签,参数multiple为是否开启批量确认
            }
        };
        channel.basicConsume(Constants.RPC_REQUEST_QUEUE,false,consumer);// 改为手动确认, 自动确认的话, 收到消息就会立马从RabbitMQ删除
    }
}

(4) 公共代码

java 复制代码
package com.ran.constant;

/**
 * Created with IntelliJ IDEA.
 * Description:
 * User: ran
 * Date: 2026-03-22
 * Time: 18:47
 */
public class Constants {
    public static final String HOST = "localhost";
    public static final int PORT = 5672;
    public static final String USER_NAME = "admin";
    public static final String PASSWORD = "admin";
    public static final String VIRTUAL_HOST = "ran";

    public static final String SIMPLE_QUEUE = "hello"; // 普通模式-队列

    public static final String WORK_QUEUE = "work.queue"; // 工作模式-队列

    public static final String FANOUT_EXCHANGE = "fanout.exchange"; // 广播/订阅模式-交换机

    public static final String FANOUT_QUEUE1 = "fanout.queue1"; // 广播/订阅模式-队列1
    public static final String FANOUT_QUEUE2 = "fanout.queue2"; // 广播/订阅模式-队列2

    public static final String DIRECT_EXCHANGE = "direct.exchange"; // 路由模式-交换机
    public static final String DIRECT_QUEUE1 = "direct.queue1"; // 路由模式-队列1
    public static final String DIRECT_QUEUE2 = "direct.queue2"; // 路由模式-队列2

    public static final String TOPIC_EXCHANGE = "topic.exchange"; // 通配符模式-交换机
    public static final String TOPIC_QUEUE1 = "topic.queue1"; // 通配符模式-队列1
    public static final String TOPIC_QUEUE2 = "topic.queue2"; // 通配符模式-队列2

    public static final String RPC_REQUEST_QUEUE = "rpc.request.queue"; // RPC模式-队列1
    public static final String RPC_RESPONSE_QUEUE = "rpc.response.queue"; // RPC模式-队列2

}

(5) 效果图

客户端

服务器

7. Publisher Confirms(发布确认)

(1) 解释

1. 是RabbitMQ提供的一种确保消息可靠发送到RabbitMQ服务器的机制, 可以确保生产者的消息被RabbitMQ服务器成功接收, 避免丢失, 安全性较高
2. 生产者将通道(Channel)设置为Confirm模式, 每一条消息都会获得一个独特的ID
3. 当消息被RabbitMQ服务器接收储存后, 会异步向生产者发送一个包含独特ID的确认信息(ACK), 来告诉生产者, 消息已经到达

(2) 生产者代码

(3) 消费者代码

相关推荐
若水不如远方2 小时前
分布式一致性(七):架构角度 —— 分布式共识系统的选型指南
分布式·后端
&&月弥4 小时前
三大开源消息队列(Kafka、RabbitMQ、RocketMQ)使用教程
kafka·开源·rabbitmq
Darkdreams4 小时前
分布式监控Skywalking安装及使用教程(保姆级教程)
分布式·skywalking
深蓝电商API13 小时前
分布式事务在跨境交易中的解决方案
分布式·跨境电商·代购系统·反向海淘·代购平台·跨境代购
我真会写代码18 小时前
从入门到精通:Kafka核心原理与实战避坑指南
分布式·缓存·kafka
黄俊懿19 小时前
【架构师从入门到进阶】第二章:系统衡量指标——第一节:伸缩性、扩展性、安全性
分布式·后端·中间件·架构·系统架构·架构设计
一叶飘零_sweeeet19 小时前
击穿 Kafka 高可用核心:分区副本、ISR 机制与底层原理全链路拆解
分布式·架构·kafka
007张三丰21 小时前
常用缓存技术全方位解析:从本地缓存到分布式缓存
分布式·缓存
tianyuanwo1 天前
Koji 分布式编译调度机制深度解析:多架构异构节点的资源优化方案
分布式·架构