RabbitMQ
MQ 概念
Message Queue - 消息队列,是消息再传输过程中进行保存的容器,用于分布式系统之间的通信,进行异步消息传递。
- 优势
- 解耦和解偶: 消息队列可以实现生产者和消费者之间的解耦,使它们在时间上和空间上解耦。生产者和消费者不需要直接知道对方的存在,只需要关注消息的发送和接收。
- 异步通信: 消息队列允许异步通信,发送者可以继续执行而不必等待接收者的响应。这提高了系统的整体性能和响应速度。
- 流量削峰: 当系统面临突发的高峰流量时,消息队列可以用来平滑流量,防止系统过载。消息队列可以缓冲突发的请求,按照系统的处理能力逐渐处理。
- 数据持久化: 大多数消息队列提供消息的持久化选项,确保即使在系统故障或重启后,消息也不会丢失。
- 可靠性: 消息队列通常具有高度的可靠性和稳定性。消息被安全地存储在队列中,而消费者可以在准备好的时候进行处理。
- 灵活性: 消息队列提供了多种消息传递模式,如点对点、发布-订阅等,使得它适用于不同的应用场景
- 劣势
- 复杂性: 引入消息队列会增加系统的复杂性,需要额外的组件和管理。配置、监控和维护消息队列系统可能需要更多的工作。
- 一致性: 在某些情况下,由于网络故障或者消息队列本身的问题,可能导致消息的不一致性。一些消息队列系统提供了一致性保证,但需要在设计中考虑这一点。
- 延迟: 使用消息队列引入了一些传递消息的延迟。对于需要实时响应的场景,这可能是一个问题。
- 适合使用MQ的场景:
- 异步通信需求: 当系统中的组件需要异步通信,而不希望直接耦合在一起时,消息队列是一个不错的选择。
- 流量控制和削峰: 在面对突发流量或需要对流量进行精细控制的场景下,消息队列可以用来平滑流量,防止系统崩溃。
- 任务分发: 当需要将任务分发给多个消费者进行处理时,消息队列可以有效地进行任务分发和负载均衡。
- 日志处理: 在大规模系统中,将日志异步写入消息队列,然后由后台处理程序进行处理,是一种有效的日志处理方式。
- 事件驱动架构: 对于需要实现事件驱动架构的系统,消息队列是一个重要的组件,用于在不同的服务之间传递事件。
RabbitMQ概念
RabbitMQ(Rabbit Message Queue)是一种开源的消息代理软件,它实现了高级消息队列协议(AMQP)标准,用于在分布式系统中进行消息传递。以下是关于RabbitMQ的一些重要信息:
- 消息队列(Message Queue): RabbitMQ是一个消息队列的实现,它允许不同的应用程序或服务之间通过消息进行通信。消息队列提供了一种异步通信机制,发送者(producer)将消息发送到队列,接收者(consumer)从队列中取出消息进行处理。
- AMQP协议: RabbitMQ采用了AMQP协议,这是一种开放的消息协议标准,用于在分布式系统中进行消息传递。AMQP定义了消息的格式和传递方式,使得不同系统之间可以进行可靠的、跨平台的消息传递。
- 发布-订阅模型: RabbitMQ支持发布-订阅模型,其中一个消息可以被多个消费者订阅。这种模型使得多个应用程序可以监听同一消息,并采取相应的处理。
- 消息持久化: RabbitMQ允许消息进行持久化,确保消息不会因为代理的重启而丢失。这对于一些关键的业务场景非常重要。
- Exchange和Queue: 在RabbitMQ中,消息通过交换机(Exchange)来路由到一个或多个队列(Queue)。Exchange定义了消息的路由规则,而Queue则存储消息直到消费者准备处理它们。
- 灵活的路由: RabbitMQ的灵活路由机制允许根据不同的条件将消息路由到不同的队列。这使得消息可以根据其类型、优先级等属性进行有效的管理。
- 可扩展性: RabbitMQ是一个可扩展的消息系统,可以通过添加多个节点来增加其容量和可用性。这使得它适用于处理大规模系统的消息通信。
- 多语言支持: RabbitMQ提供了多种语言的客户端库,包括Java、Python、Ruby、.NET等,使得开发者可以在各种不同的编程语言中使用RabbitMQ。
六种工作模式
- 简单模式(Simple Mode):
- 定义: 单一生产者将消息发送到一个队列,单一消费者从队列中接收并处理消息。
- 工作原理: 生产者发送消息到队列,消费者从队列中获取消息进行处理。
- 优点: 简单直观,适用于基本的消息传递场景。
- 缺点: 缺乏扩展性,只有一个消费者能够处理消息。
- 注意事项: 适用于单一任务的场景,不适用于需要处理大量消息或需要分布式处理的情况
- 工作队列模式(Work Queues):
- 定义: 多个消费者共享一个队列,生产者发送消息到该队列,多个消费者并发处理消息。
- 工作原理: 消息被轮流发送到不同的消费者。
- 优点: 可以提高消息处理的并发性。
- 缺点: 消息的负载在消费者之间可能不均衡。
- 注意事项: 可以通过设置合适的负载均衡策略来解决不均衡的问题,确保每个消费者处理的消息数量大致相同。
- 发布-订阅模式(Publish-Subscribe):
- 定义: 生产者发送消息到交换机,交换机将消息广播到与之绑定的所有队列。
- 工作原理: 多个队列订阅同一个交换机,每个队列都有一个独立的消费者。
- 优点: 可以实现广播消息给多个消费者。
- 缺点: 消费者无法选择性地接收消息。
- 注意事项: 适用于需要将消息广播给多个消费者的场景,但不适用于需要选择性接收消息的情况。
- 路由模式(Routing):
- 定义: 生产者发送消息到交换机,并通过路由键标记消息,交换机根据路由键将消息发送到相应的队列。
- 工作原理: 交换机根据路由键将消息发送到匹配的队列。
- 优点: 可以根据路由键选择性地将消息发送到特定的队列。
- 缺点: 需要提前定义好路由规则。
- 注意事项: 适用于根据一些条件选择性地将消息发送到不同的队列的场景。
- 主题模式(Topics):
- 定义: 类似于路由模式,但使用通配符来匹配路由键。
- 工作原理: 生产者发送消息到交换机,并指定一个主题作为路由键,消费者使用通配符匹配感兴趣的主题。
- 优点: 提供更灵活的路由规则,支持通配符匹配。
- 缺点: 需要消费者理解和使用通配符规则。
- 注意事项: 适用于需要更灵活的路由规则,以及根据主题进行消息过滤的场景。
- 远程过程调用模式(Remote Procedure Call - RPC):
- 情况: 生产者调用远程服务,将消息发送到队列,远程服务消费消息并执行相应的远程过程。
- 应用场景: 适用于需要通过消息调用远程服务的情况,允许分布式系统中的组件进行通信。
- 隐患: 需要处理远程调用可能出现的延迟、错误和顺序性问题。通信的异步性可能导致调用方难以确定远程调用的结果。RPC需要考虑服务的可用性和一致性。
RabbitMQ 安装与配置
环境准备
- CentOS-7 :Linux 操作系统,软件运行环境
- otp_src_19.3.tar.gz :erLang 语言支持
- rabbitmq-server-3.7.2-1.el7.noarch.rpm :RabbitMQ 服务
CentOS-7 安装
通过VMware Worksation虚拟机安装下载的Centos7镜像文件
安装ifconfig命令
-
输入命令dhclient,可以自动获取一个IP地址,再用命令ip -a addr查看IP
-
然后输入 yum search ifconfig查找符合这个命令的组件,查找到net-tools.x86_64,安装这个组件
-
yum install net-tools.x86_64
-
安装成功后 输入 ifconfig 测试
-
安装必要的软件
yum install wget vim unzip net-tools -y
-
关闭防火墙
systemctl disable firewalld systemctl stop firewalld
-
关闭 SELinux
vi /etc/selinux/config
, 修改配置文件保存后退出SELINUX=disabled
-
reboot重启
RabbitMQ 安装
- 安装必要的依赖
yum install gcc glibc-devel make ncurses-devel openssl-devel xmlto -y
-
通过putty或者windows terminal ssh远程连接ip地址将安装包放到 Linux 的家目录里
-
通过shell脚本进行安装
#!/bin/bash
# setup erLang & RabbitMQ
tar -zxvf otp_src_19.3.tar.gz
mkdir /usr/local/erlang
cd otp_src_19.3
./configure --prefix=/usr/local/erlang --without-javac
make && make install
echo 'ERL_HOME=/usr/local/erlang'>>/etc/profile
echo 'PATH=$ERL_HOME/bin:$PATH'>>/etc/profile
echo 'export ERL_HOME PATH'>>/etc/profile
cd ~
rpm -ivh --nodeps rabbitmq-server-3.7.2-1.el7.noarch.rpm
# source 命令只能在当前的 shell 脚本当中生效,在新的脚本中没有作用
source /etc/profile
- 创建配置文件
vi /etc/rabbitmq/rabbitmq-env.conf
添加内容
NODENAME=rabbit@localhost
- 通过
rabbitmq-server
启动并查看
RabbitMQ 常用命令
启动关闭和查询
rabbitmq-server start &
添加 &
是后台启动
rabbitmqctl stop
ps -ef | grep rabbitmq
插件管理
- 添加插件
rabbitmq-plugins enable {插件名}
- 删除插件
rabbitmq-plugins disable {插件名}
浏览器管理控制台的使用
注意:RabbitMQ 启动以后可以使用浏览器进入管控台,但是默认情况RabbitMQ 不允许直接使用浏览器浏览器进行访问,因此必须添加插件
rabbitmq-plugins enable rabbitmq_management
- 再次启动RabbitMq
rabbitmq-server start &
- 插件添加后现在可以通过浏览器进行访问,RabbitMQ服务对应的端口号为15672
访问地址:https://{个人主机地址}:15672
用户管理
使用默认用户名guest登录
username:guest
password:guest
默认guest用户登录只允许本机访问登录,如果采用远程访问登录需要在命令行中输入如下指令
-
添加用户:
- 语法:
rabbitmqctl add_user {username} {password}
- 语法:
-
删除用户:
- 语法:
rabbitmqctl delete_user {username}
- 语法:
-
修改密码:
- 语法:
rabbitmqctl change_password {username} {newpassword}
- 语法:
-
设置用户角色:
- 语法:
rabbitmqctl set_user_tags {username} {tag}
- tag为用户角色,一般设置
administrator
- 语法:
测试:
添加一个管理员用户 rabbitmqctl add_user root toor
设置用户角色 rabbitmqctl set_user_tags root administrator
,再次登录
RabbitMQ消息收发
RabbitMQ消息收发通过生产者将消息发布到交换机,交换机将消息路由到队列,然后消费者从队列中接收并处理消息,实现了可靠的异步消息传递。
生产者(Publisher): 负责创建并发布消息到 RabbitMQ 代理中。
消息代理(Broker): RabbitMQ 作为消息代理,负责接收、存储和路由消息。
虚拟主机(Virtual Host): 用于隔离不同的消息环境,提供安全性和多租户支持。
交换机(Exchange): 接收来自生产者发布的消息,并根据路由规则将消息传递给一个或多个队列。
绑定(Binding): 将交换机与队列之间建立关联,确保消息能够正确地路由到特定的队列。
队列(Queue): 用于存储消息,等待消费者来获取并处理这些消息。
连接(Connection): 表示生产者或消费者与 RabbitMQ 代理的网络连接。
信道(Channel): 是在连接中创建的独立通信信道,用于在连接内部进行多路复用以提高效率。
消费者(Consumer): 从队列中获取消息并进行处理的应用程序组件。
Exchange 类型
在 RabbitMQ 中,交换机(Exchange)是消息的路由器,负责将接收到的消息路由到一个或多个队列。RabbitMQ 提供了不同类型的交换机,每种类型都有不同的路由规则。以下是 RabbitMQ 支持的几种交换机类型:
1.Direct Exchange: 直接交换机是最简单的交换机类型。它根据消息的路由键(Routing Key)将消息直接路由到与之绑定的队列。如果交换机的路由键与消息的路由键完全匹配,那么消息将被路由到相应的队列。
2.Fanout Exchange: 扇出交换机将接收到的消息广播到所有与之绑定的队列,忽略消息的路由键。这意味着不管消息的路由键是什么,都会被发送到所有与交换机绑定的队列中。
3.Topic Exchange: 主题交换机允许更灵活的路由。它使用通配符进行路由键匹配。在绑定队列时,可以使用通配符指定路由键的模式,使得交换机能够将消息路由到符合指定模式的队列中。
4.Headers Exchange: 头交换机使用消息的头部信息进行路由。在绑定队列时,可以指定一组键值对,只有当消息的头部信息与这些键值对完全匹配时,消息才会被路由到相应的队列。
5.Default Exchange: RabbitMQ 提供一个默认的交换机,也称为无名交换机。它是一个直连交换机,其路由键为队列的名称。当消息的路由键与队列的名称匹配时,消息将被路由到相应的队列。
Direct Exchange
- 所有发送到Direct Exchange的消息被转发到RouteKey中指定的Queue,即和routeKey一一对应。
- Direct模式可以使用RabbitMQ自带的Exchange:
default Exchange
,所以不需要将Exchange进行任何绑定操作,消息传递时,routeKey必须完全匹配才会被队列接收,否则该消息会被抛弃
Fanout Exchange
- 不处理路由键,只需要简单的将队列绑定到交换机上
- 发送到交换机的消息都会被转发到与该交换机绑定的所有队列上
- Fanout交换机转发消息是最快的,它不会做各种路由匹配
Topic Exchange
- 交换器通过模式匹配分配消息的路由键属性,将路由键和某个模式进行匹配,此时队列需要绑定到一个模式上。它将路由键和绑定键的字符串切分成单词,这些单词之间用点隔开。
- 识别两个通配符:
- 符号
#
:匹配一个或多个词,例如tag.#
能够匹配到tag.a.b
- 符号
*
:只匹配一个词,例如tag.*
只会匹配到tag.a
- 符号
MQ收发消息 - 不使用 Exchange
- 向 MQ 发送消息
- 新建 Maven 模块
MQ-01-Send
- 添加依赖
pom.xml
<dependencies>
<dependency>
<groupId>com.rabbitmq</groupId>
<artifactId>amqp-client</artifactId>
<version>5.14.2</version>
</dependency>
</dependencies>
- 发送代码
Send.java
package com.itxw;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
/**
* @Classname Send
* @Description: TODO
* @Author: User
*/
public class Send {
public static void main(String[] args){
// 创建连接工厂,用于指定 RabbitMQ 的连接信息
ConnectionFactory factory = new ConnectionFactory();
// 设置参数
factory.setHost("192.168.184.128");
factory.setPort(5672);
factory.setUsername("root");
factory.setPassword("toor");
// 新建连接
Connection connection = null;
// 创建通道
Channel channel = null;
try {
connection = factory.newConnection();
channel = connection.createChannel();
// 声明队列
String queueName = "myQueue";
/**
* 声明队列
* 参数一:队列名
* 参数二:是否支持持久化
* 参数三:是否排外,true 表示排外,如果消费者监听了这个队列,则不允许其他消费者监听此队列
* 参数四:是否删除,true 表示自动删除,如果没有消费者监听这个队列则自动删除
* 参数五:队列的属性设置,通常设置为 null
*/
channel.queueDeclare(queueName,true,false,false,null);
/**
* 发送消息到 MQ
* 参数一:交换机名,不使用交换机则填写空串 ""
* 参数二:消息所携带的 RoutingKey ,不使用交换机,此参数会被识别成队列名
* 参数三:消息的属性,通常设置为 null
* 参数四:具体消息数据取值,byte[] 类型
*/
String message = "this is a test message !";
channel.basicPublish("",queueName,null,message.getBytes());
System.out.println("发送成功!");
} catch (IOException e) {
e.printStackTrace();
} catch (TimeoutException e) {
e.printStackTrace();
}finally {
if(null!=channel){
try {
channel.close();
} catch (IOException e) {
e.printStackTrace();
} catch (TimeoutException e) {
e.printStackTrace();
}
}
if(null!=connection){
try {
channel.close();
} catch (IOException e) {
e.printStackTrace();
} catch (TimeoutException e) {
e.printStackTrace();
}
}
}
}
}
- 运行测试
- 从 MQ 接收消息
- 新建 Maven 模块
MQ-01-Receive
,创建pom.xml
同上 - 接收代码
Receive.java
package com.itxw;
import com.rabbitmq.client.*;
import com.sun.xml.internal.ws.wsdl.writer.document.soap.Body;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
/**
* @Classname Receive
* @Description: TODO
* @Author: User
*/
public class Receive {
public static void main(String[] args) {
// 创建连接工厂,用于指定 RabbitMQ 的连接信息
ConnectionFactory factory = new ConnectionFactory();
// 设置参数
factory.setHost("192.168.184.128");
factory.setPort(5672);
factory.setUsername("root");
factory.setPassword("toor");
// 新建连接
Connection connection = null;
// 创建通道
Channel channel = null;
try {
connection = factory.newConnection();
channel = connection.createChannel();
// 声明队列
String queueName = "myQueue";
/**
* 声明队列
* 参数一:队列名
* 参数二:是否支持持久化
* 参数三:是否排外,true 表示排外,如果消费者监听了这个队列,则不允许其他消费者监听此队列
* 参数四:是否删除,true 表示自动删除,如果没有消费者监听这个队列则自动删除
* 参数五:队列的属性设置,通常设置为 null
*/
channel.queueDeclare(queueName,true,false,false,null);
channel.basicConsume(queueName,true,new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
// super.handleDelivery(consumerTag, envelope, properties, body);
String info = new String(body);
System.out.println("接收到的内容为: " + info);
}
});
System.out.println("接收成功!");
} catch (IOException e) {
e.printStackTrace();
} catch (TimeoutException e) {
e.printStackTrace();
}
}
}
- 运行测试
MQ收发消息 - 使用 Exchange
基于direct的收发
- 新建 Maven 模块
MQ-02-DirectSend
,创建pom.xml
同上 - 发送代码
DirectSend.java
package com.itxw;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
/**
* @Classname directSend
* @Description: TODO
* @Author: User
*/
public class DirectSend {
public static void main(String[] args) {
// 创建连接工厂,用于指定 RabbitMQ 的连接信息
ConnectionFactory factory = new ConnectionFactory();
// 设置参数
factory.setHost("192.168.184.128");
factory.setPort(5672);
factory.setUsername("root");
factory.setPassword("toor");
// 新建连接
Connection connection = null;
// 创建通道
Channel channel = null;
try {
connection = factory.newConnection();
channel = connection.createChannel();
// 声明队列
String queueName = "myDirectQueue";
/**
* 声明队列
* 参数一:队列名
* 参数二:是否支持持久化
* 参数三:是否排外,true 表示排外,如果消费者监听了这个队列,则不允许其他消费者监听此队列
* 参数四:是否删除,true 表示自动删除,如果没有消费者监听这个队列则自动删除
* 参数五:队列的属性设置,通常设置为 null
*/
channel.queueDeclare(queueName,true,false,false,null);
/**
* 声明交换机
* 参数一:交换机名称,如果不存在则创建,存在则放弃
* 参数二:交换机类型,取值为:direct、fanout、topic、headers
* 参数三:是否持久化
*/
String exchangeName = "directExchange";
channel.exchangeDeclare(exchangeName,"direct",true);
/**
* 将队列和交换机绑定
* 参数一:队列名
* 参数二:交换机名
* 参数三:BindingKey
*/
channel.queueBind(queueName,exchangeName,"directKey");
/**
* 发送消息到 MQ
* 参数一:交换机名,不使用交换机则填写空串 ""
* 参数二:消息所携带的 RoutingKey ,不使用交换机,此参数会被识别成队列名
* 参数三:消息的属性,通常设置为 null
* 参数四:具体消息数据取值,byte[] 类型
*/
String message = "this is a direct test message !";
channel.basicPublish(exchangeName,"directKey",null,message.getBytes());
System.out.println("发送成功!");
} catch (IOException e) {
e.printStackTrace();
} catch (TimeoutException e) {
e.printStackTrace();
}finally {
if(null!=channel){
try {
channel.close();
} catch (IOException e) {
e.printStackTrace();
} catch (TimeoutException e) {
e.printStackTrace();
}
}
if(null!=connection){
try {
connection.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
- 运行测试
- 新建 Maven 模块
MQ-02-DirectReceive
,创建pom.xml
同上 - 接收代码
DirectReceive.java
package com.itxw;
import com.rabbitmq.client.*;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
/**
* @Classname DirectReceive
* @Description: TODO
* @Author: User
*/
public class DirectReceive {
public static void main(String[] args) {
// 创建连接工厂,用于指定 RabbitMQ 的连接信息
ConnectionFactory factory = new ConnectionFactory();
// 设置参数
factory.setHost("192.168.184.128");
factory.setPort(5672);
factory.setUsername("root");
factory.setPassword("toor");
// 新建连接
Connection connection = null;
// 创建通道
Channel channel = null;
try {
connection = factory.newConnection();
channel = connection.createChannel();
// 声明队列
String queueName = "myDirectQueue";
channel.queueDeclare(queueName,true,false,false,null);
// 声明交换机
String exchangeName = "myDirectExchange";
channel.exchangeDeclare(exchangeName,"direct",true);
// 绑定
channel.queueBind(queueName,exchangeName,"directKey");
// 接收对应 routingKey 队列消息
channel.basicConsume(queueName,true,"directKey",new DefaultConsumer(channel){
/**
* 消息处理的回调方法
* @param consumerTag 消费者的唯一标识
* @param envelope 消息的特征或特性,这个对象可以获取当前消息的编号,以及是否被接受过
* @param properties 消息携带的属性,这个属性是发送的时候传递过来的
* @param body 接收到的具体消息内容
* @throws IOException
*/
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
// super.handleDelivery(consumerTag, envelope, properties, body);
String info = new String(body);
System.out.println("接收到的内容为: " + info);
}
});
System.out.println("接收消息中...");
} catch (IOException e) {
e.printStackTrace();
} catch (TimeoutException e) {
e.printStackTrace();
}
}
}
基于fanout的收发
- 新建 Maven 模块
MQ-03-FanoutSend
,创建pom.xml
同上 - 发送代码
FanoutSend.java
package com.itxw;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
/**
* @Classname FanoutReceive
* @Description: TODO
* @Author: User
*/
public class FanoutSend {
public static void main(String[] args) {
// 创建连接工厂,用于指定 RabbitMQ 的连接信息
ConnectionFactory factory = new ConnectionFactory();
// 设置参数
factory.setHost("192.168.184.128");
factory.setPort(5672);
factory.setUsername("root");
factory.setPassword("toor");
// 新建连接
Connection connection = null;
// 创建通道
Channel channel = null;
try {
connection = factory.newConnection();
channel = connection.createChannel();
// 声明交换机
String exchangeName = "myFanoutExchange";
channel.exchangeDeclare(exchangeName,"fanout",true);
// 发送端是广播的形式进行发送,没有声明队列则不需要绑定交换机与队列
String message = "this is a fanout test message !";
channel.basicPublish(exchangeName,"",null,message.getBytes());
System.out.println("发送成功!");
} catch (IOException e) {
e.printStackTrace();
} catch (TimeoutException e) {
e.printStackTrace();
}finally {
if(null!=channel){
try {
channel.close();
} catch (IOException e) {
e.printStackTrace();
} catch (TimeoutException e) {
e.printStackTrace();
}
}
if(null!=connection){
try {
connection.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
- 新建 Maven 模块
MQ-03-FanoutReceive
,创建pom.xml
同上 - 接收代码
FanoutReceive.java
package com.itxw;
import com.rabbitmq.client.*;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
/**
* @Classname FanoutReceive
* @Description: TODO
* @Author: User
*/
public class FanoutReceive {
public static void main(String[] args) {
// 创建连接工厂,用于指定 RabbitMQ 的连接信息
ConnectionFactory factory = new ConnectionFactory();
// 设置参数
factory.setHost("192.168.184.128");
factory.setPort(5672);
factory.setUsername("root");
factory.setPassword("toor");
// 新建连接
Connection connection = null;
// 创建通道
Channel channel = null;
try {
connection = factory.newConnection();
channel = connection.createChannel();
// 声明一个队列,并获取队列的名称
String queueName = channel.queueDeclare().getQueue();
// 声明交换机
String exchangeName = "myFanoutExchange";
channel.exchangeDeclare(exchangeName,"fanout",true);
/**
* 发送消息时,你只需要将消息发布到 Fanout Exchange,而不需要指定目标队列。这是因为 Fanout Exchange 广播消息到所有与之绑定的队列,而不依赖于消息的路由键。
*
* 但是,接收消息的消费者需要有一个地方来存储和接收这些广播的消息,这就是队列的作用。因此,在消费者端,你需要创建一个队列,并将其绑定到 Fanout Exchange 上,以便能够接收广播的消息。
*/
channel.queueBind(queueName, exchangeName, "");
// 接收消息
channel.basicConsume(queueName,true,new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
// super.handleDelivery(consumerTag, envelope, properties, body);
String info = new String(body);
System.out.println("接收到的内容为: " + info);
}
});
System.out.println("接收消息中...");
} catch (IOException e) {
e.printStackTrace();
} catch (TimeoutException e) {
e.printStackTrace();
}
}
}
- 进行测试
基于topic的收发
- 新建 Maven 模块
MQ-04-TopicSend
,创建pom.xml
同上 - 发送代码
TopicSend.java
package com.itxw;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
/**
* @Classname TopicReceive
* @Description: TODO
* @Author: User
*/
public class TopicSend {
public static void main(String[] args) {
// 创建连接工厂,用于指定 RabbitMQ 的连接信息
ConnectionFactory factory = new ConnectionFactory();
// 设置参数
factory.setHost("192.168.184.128");
factory.setPort(5672);
factory.setUsername("root");
factory.setPassword("toor");
// 新建连接
Connection connection = null;
// 新建通道
Channel channel = null;
try {
connection = factory.newConnection();
channel = connection.createChannel();
// 声明交换机
String exchangeName = "myTopicExchange";
channel.exchangeDeclare(exchangeName,"topic",true);
//
// String routingKey = "tag";
// String routingKey = "tag.a";
String routingKey = "tag.a.b";
String message = "this is a topic test message ";
channel.basicPublish(exchangeName,routingKey,null,message.getBytes());
System.out.println("发送成功 !");
} catch (IOException e) {
e.printStackTrace();
} catch (TimeoutException e) {
e.printStackTrace();
}finally {
if(null!=channel){
try {
channel.close();
} catch (IOException e) {
e.printStackTrace();
} catch (TimeoutException e) {
e.printStackTrace();
}
}
if(null!=connection){
try {
connection.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
- 新建 Maven 模块
MQ-04-TopicReceive
,创建pom.xml
同上 - 接收代码
TopicReceive.java
package com.itxw;
import com.rabbitmq.client.*;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
/**
* @Classname TopicReceive
* @Description: TODO
* @Author: User
*/
public class TopicReceive01 {
public static void main(String[] args) {
// 创建连接工厂,用于指定 RabbitMQ 的连接信息
ConnectionFactory factory = new ConnectionFactory();
// 设置参数
factory.setHost("192.168.184.128");
factory.setPort(5672);
factory.setUsername("root");
factory.setPassword("toor");
// 新建连接
Connection connection = null;
// 新建通道
Channel channel = null;
try {
connection = factory.newConnection();
channel = connection.createChannel();
String queueName = channel.queueDeclare().getQueue();
// 声明交换机
String exchangeName = "myTopicExchange";
channel.exchangeDeclare(exchangeName,"topic",true);
// 绑定
String routingKey = "tag";
// String routingKey = "tag.*";
// String routingKey = "tag.#";
channel.queueBind(queueName,exchangeName,routingKey);
// 接收消息
channel.basicConsume(queueName,true,new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
// super.handleDelivery(consumerTag, envelope, properties, body);
String info = new String(body);
System.out.println("接收的消息为 tag ---> " + info );
//System.out.println("接收的消息为 tag.* ---> " + info );
//System.out.println("接收的消息为 tag.# ---> " + info );
}
});
System.out.println("接收消息中...");
} catch (IOException e) {
e.printStackTrace();
} catch (TimeoutException e) {
e.printStackTrace();
}
}
}
RabbitMQ 事务机制
事务机制确保一组消息要么全部成功地被发布到队列,要么全部失败。其本质就是防止消息丢失。
- 通过 AMQP 提供的事物机制来实现
- 使用发送者确认模式来实现
方法 | 作用 | 注意 |
---|---|---|
channel.txSelect() | 开启事务 | 必须要显示到调用 txCommit() 或者 txRollback() |
channel.txCommit() | 提交事务 | 开启事务后需要显示,否则消息不会进入队列 |
channel.txRollback() | 事务回滚 | 回滚事务必须在channel关闭之前 |
事务的简单使用
transactionSend.java
package com.itxw;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
/**
* @Classname transactionSend
* @Description: TODO
* @Author: User
*/
public class transactionSend {
public static void main(String[] args) {
// 创建连接工厂,用于指定 RabbitMQ 的连接信息
ConnectionFactory factory = new ConnectionFactory();
// 设置参数
factory.setHost("192.168.184.128");
factory.setPort(5672);
factory.setUsername("root");
factory.setPassword("toor");
// 新建连接
Connection connection = null;
// 创建通道
Channel channel = null;
try {
connection = factory.newConnection();
channel = connection.createChannel();
// 声明队列
String queueName = "transactionQueue";
channel.queueDeclare(queueName,true,false,false,null);
// 声明交换机
String exchangeName = "transactionExchange";
channel.exchangeDeclare(exchangeName,"direct",true);
// 绑定
channel.queueBind(queueName,exchangeName,"transactionKey");
// 开启事务
channel.txSelect();
// 发送消息
String message = "this is a transaction test message !";
channel.basicPublish(exchangeName,"transactionKey",null,message.getBytes());
// 提交事务
channel.txCommit();
System.out.println("发送成功!");
} catch (IOException e) {
e.printStackTrace();
} catch (TimeoutException e) {
e.printStackTrace();
}finally {
// 通道为空,消息发送成功,不为空,则进行事务的回滚
if(null!=channel){
try {
// 事务回滚
channel.txRollback();
channel.close();
} catch (IOException e) {
e.printStackTrace();
} catch (TimeoutException e) {
e.printStackTrace();
}
}
if(null!=connection){
try {
connection.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
transactionReceive.java
package com.itxw;
import com.rabbitmq.client.*;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
/**
* @Classname transactionReceive
* @Description: TODO
* @Author: User
*/
public class transactionReceive {
public static void main(String[] args) {
// 创建连接工厂,用于指定 RabbitMQ 的连接信息
ConnectionFactory factory = new ConnectionFactory();
// 设置参数
factory.setHost("192.168.184.128");
factory.setPort(5672);
factory.setUsername("root");
factory.setPassword("toor");
// 新建连接
Connection connection = null;
// 创建通道
Channel channel = null;
try {
connection = factory.newConnection();
channel = connection.createChannel();
// 声明队列
String queueName = "transactionQueue";
channel.queueDeclare(queueName,true,false,false,null);
// 声明交换机
String exchangeName = "transactionExchange";
channel.exchangeDeclare(exchangeName,"direct",true);
// 绑定
channel.queueBind(queueName,exchangeName,"transactionKey");
// 接收消息
channel.basicConsume(queueName,true,new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
// super.handleDelivery(consumerTag, envelope, properties, body);
String message = new String(body);
System.out.println("接收的消息为 :" + message);
}
});
System.out.println("接收消息中....");
} catch (IOException e) {
e.printStackTrace();
} catch (TimeoutException e) {
e.printStackTrace();
}
}
}
三种发送确认
消息的发送确认方式和事务类似,也是通过channel进行发送确认的,但该方式和事务有着本质的区别在于:发送消息丢失的时候不会像事务一样停止发送消息,而是补发消息直到消息发送到对方确认为止。 需要注意的是,发送确认模式可能会造成 MQ 中的消息重复,消费者中需要拥有重复消息处理的能力。
方式 | 说明 |
---|---|
channel.waitForConfirms() | 普通发送消息确认模式,该模式是不断的等待接收方来返回消息,如果有返回消息则说明消息发送成功 |
channel.waitForConfirmsOrDie() | 函数批量确认模式 |
channel.addConfirmListener() | 异步监听发送确认模式,需要new ConfirmListener()来实现内部回调函数 |
channel.confirmSelect()
- - - 开启以上发送确认方式
方式一
channel.waitForConfirms()
package com.itxw;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
/**
* @Classname confirmSend01
* @Description: TODO
* @Author: User
*/
public class confirmSend01 {
public static void main(String[] args) {
// 创建连接工厂,用于指定 RabbitMQ 的连接信息
ConnectionFactory factory = new ConnectionFactory();
// 设置参数
factory.setHost("192.168.184.128");
factory.setPort(5672);
factory.setUsername("root");
factory.setPassword("toor");
// 新建连接
Connection connection = null;
// 创建通道
Channel channel = null;
try {
connection = factory.newConnection();
channel = connection.createChannel();
// 声明队列
String queueName = "confirmQueue";
channel.queueDeclare(queueName,true,false,false,null);
// 声明交换机
String exchangeName = "confirmExchange";
channel.exchangeDeclare(exchangeName,"direct",true);
// 绑定
channel.queueBind(queueName,exchangeName,"confirmKey");
// 开启发送确认模式
channel.confirmSelect();
// 发送消息
String message = "this is a confirm01 test message !";
channel.basicPublish(exchangeName,"confirmKey",null,message.getBytes());
/**
* channel.waitForConfirms()返回的是boolean类型,是一个阻塞式方法,会堵塞线程等待服务器来返回确认消息,
* 可以为这个函数指定一个毫秒值用于等待服务器的确认超时时间,避免卡死。如果抛出异常表示服务器出了问题,需要补发消息。
* 无论是返回false还是抛出异常都有可能消息发送成功或者没有发送成功。
* 补发消息可以将消息缓存到Redis中稍后使用定时任务来补发,或者使用递归的方法来补发。
*/
try {
if(!channel.waitForConfirms(5000L)){
System.out.println("需要重新补发消息");
}
} catch (InterruptedException e) {
// 异常
e.printStackTrace();
System.out.println("需要重新补发消息");
}catch (TimeoutException e) {
// 超时
e.printStackTrace();
System.out.println("需要重新补发消息");
}
System.out.println("发送成功!");
} catch (IOException e) {
e.printStackTrace();
} catch (TimeoutException e) {
e.printStackTrace();
}finally {
if(null!=channel){
try {
channel.close();
} catch (IOException e) {
e.printStackTrace();
} catch (TimeoutException e) {
e.printStackTrace();
}
}
if(null!=connection){
try {
connection.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
方式二
channel.waitForConfirmsOrDie()
package com.itxw;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
/**
* @Classname confirmSend01
* @Description: TODO
* @Author: User
*/
public class confirmSend02 {
public static void main(String[] args) {
// 创建连接工厂,用于指定 RabbitMQ 的连接信息
ConnectionFactory factory = new ConnectionFactory();
// 设置参数
factory.setHost("192.168.184.128");
factory.setPort(5672);
factory.setUsername("root");
factory.setPassword("toor");
// 新建连接
Connection connection = null;
// 创建通道
Channel channel = null;
try {
connection = factory.newConnection();
channel = connection.createChannel();
// 声明队列
String queueName = "confirmQueue";
channel.queueDeclare(queueName, true, false, false, null);
// 声明交换机
String exchangeName = "confirmExchange";
channel.exchangeDeclare(exchangeName, "direct", true);
// 绑定
channel.queueBind(queueName, exchangeName, "confirmKey");
// 开启发送确认模式
channel.confirmSelect();
// 发送消息
String message = "this is a confirm02 test message !";
channel.basicPublish(exchangeName, "confirmKey", null, message.getBytes());
/**
* channel.waitForConfirms()没有返回值
* 如果正常执行结束则成功,如果出现异常则表示失败
* 也可以指定一个毫秒值来用于等带服务器的确认时间,如果超过这个时间就抛出异常,表示确认失败需要补发
* 批量确认消息比普通确认要快,但是如果一但出现了消息补发的情况,就不能确定是哪条消息需要补发,所以就会将本次发送的所有消息进行补发。
*/
try {
channel.waitForConfirmsOrDie(5000L);
} catch (InterruptedException e) {
e.printStackTrace();
System.out.println("需要重新补发消息");
} catch (TimeoutException e) {
e.printStackTrace();
System.out.println("需要重新补发消息");
}
System.out.println("发送成功!");
} catch (IOException e) {
e.printStackTrace();
} catch (TimeoutException e) {
e.printStackTrace();
}finally {
if(null!=channel){
try {
channel.close();
} catch (IOException e) {
e.printStackTrace();
} catch (TimeoutException e) {
e.printStackTrace();
}
}
if(null!=connection){
try {
connection.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
方式三
channel.addConfirmListener()
package com.itxw;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.ConfirmListener;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
/**
* @Classname confirmSend03
* @Description: TODO
* @Author: User
*/
public class confirmSend03 {
public static void main(String[] args) {
// 创建连接工厂,用于指定 RabbitMQ 的连接信息
ConnectionFactory factory = new ConnectionFactory();
// 设置参数
factory.setHost("192.168.184.128");
factory.setPort(5672);
factory.setUsername("root");
factory.setPassword("toor");
// 新建连接
Connection connection = null;
// 创建通道
Channel channel = null;
try {
connection = factory.newConnection();
channel = connection.createChannel();
// 声明队列
String queueName = "confirmQueue";
channel.queueDeclare(queueName, true, false, false, null);
// 声明交换机
String exchangeName = "confirmExchange";
channel.exchangeDeclare(exchangeName, "direct", true);
// 绑定
channel.queueBind(queueName, exchangeName, "confirmKey");
// 开启发送确认模式
channel.confirmSelect();
/**
* 异步监听模式,需要手动创建监听器
* 注意:由于是异步监听,所以需要在监听打开后才能发送消息,方法底层会启动子线程持续监听 MQ,因此不能关闭连接以及通道
*/
channel.addConfirmListener(new ConfirmListener() {
/**
* 消息发送成功后的回调函数
* @param l 消息编号
* @param b 是否批量确认
* @throws IOException
*/
@Override
public void handleAck(long l, boolean b) throws IOException {
System.out.println("消息发送成功,消息编号:" + l + ";是否批量:" + b);
}
/**
* 消息发送失败后的回调函数
* @param l 消息编号
* @param b 是否批量确认
* @throws IOException
*/
@Override
public void handleNack(long l, boolean b) throws IOException {
System.out.println("消息发送失败,是否补发,消息编号:" + l + ";是否批量:" + b);
}
});
// 测试发送消息 50 条消息
String message = "this is a confirm03 test message !";
for (int i = 0; i < 50; i++) {
channel.basicPublish(exchangeName, "confirmKey", null, message.getBytes());
}
System.out.println("发送成功!");
} catch (IOException e) {
e.printStackTrace();
} catch (TimeoutException e) {
e.printStackTrace();
}
// 由于是异步监听,底层会启动子线程持续监听 MQ,因此不能释放通道与连接
}
}
接收确认
RabbitMQ提供了消息确认机制,接收方(消费者)使用接收确认机制来告知RabbitMQ服务器它已经成功处理了一条消息。这种机制可以通过以下步骤来实现:
- 在消费者端设置 autoAck 为 false,表示手动确认。
- 在成功处理消息后,调用 channel.basicAck(deliveryTag, false) 来确认消息。
- 如果发生错误,可以使用 channel.basicNack(deliveryTag, false, true) 进行否定确认(拒绝多条),或者使用 channel.basicReject(deliveryTag, requeue) 进行拒绝确认。
- 没有发送成功的消息可以通过basicRecover()重新发送到队列中。
package com.itxw;
import com.rabbitmq.client.*;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
/**
* @Classname confirmReceive
* @Description: TODO
* @Author: User
*/
public class confirmReceive {
public static void main(String[] args) {
// 创建连接工厂,用于指定 RabbitMQ 的连接信息
ConnectionFactory factory = new ConnectionFactory();
// 设置参数
factory.setHost("192.168.184.128");
factory.setPort(5672);
factory.setUsername("root");
factory.setPassword("toor");
// 新建连接
Connection connection = null;
// 创建通道
Channel channel = null;
try {
connection = factory.newConnection();
channel = connection.createChannel();
// 声明队列
String queueName = "confirmQueue";
channel.queueDeclare(queueName,true,false,false,null);
// 声明交换机
String exchangeName = "confirmExchange";
channel.exchangeDeclare(exchangeName,"direct",true);
// 绑定
channel.queueBind(queueName,exchangeName,"confirmKey");
channel.basicConsume(queueName,false,new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
// super.handleDelivery(consumerTag, envelope, properties, body);
try{
String info = new String(body);
System.out.println("接收到的消息为: " + info);
// 制造异常
// System.out.println(1/0);
/**
* 获取消息编号
* 根据消息的编号来确认消息
* */
long deliveryTag = envelope.getDeliveryTag();
// 确认消息
this.getChannel().basicAck(deliveryTag,true);
}catch (Exception e){
// 将没处理好的消息放到队列的尾部从,稍后重新处理
this.getChannel().basicRecover();
}
}
});
System.out.println("接收消息中.....");
} catch (IOException e) {
e.printStackTrace();
} catch (TimeoutException e) {
e.printStackTrace();
}
}
}
需要注意的是:当开启事务的时候,如果没有提交事务就导致消费者读取到的消息在消息队列中没有被移除掉。
解释:手动确认是指在消息被消费者接收并成功处理后,消费者需要发送一个明确的确认给消息队列。在这种情况下,如果你在事务中进行了回滚操作,即使你已经手动确认了消息,消息也不会被从队列中移除。这是因为在事务回滚的情况下,所有已经进行的操作都被撤销,包括手动确认。因此,消息队列认为这个消息还没有被成功处理。
SpringBoot集成RabbitMQ
基于direct的收发
- 创建发送模块,添加
Spring for RabbitMQ
依赖
application.properties
配置文件
# 配置MQ相关连接信息(单机版)
spring.rabbitmq.host=192.168.184.128
spring.rabbitmq.port=5672
spring.rabbitmq.username=root
spring.rabbitmq.password=toor
# 简化控制台日志输出格式
logging.pattern.console=%d{MM/dd-HH:mm:ss} ==> %msg%n
- 发送类
Send.java
@Component
public class Send {
// 支持一些基本的发送和接收
@Resource
private AmqpTemplate amqpTemplate;
// RabbitTemplate 是 AmqpTemplate 接口的子类
@Resource
private RabbitTemplate rabbitTemplate;
public void directSend(String message){
// rabbitTemplate.convertAndSend("directExchange","directKey",message);
amqpTemplate.convertAndSend("directExchange","directKey",message);
}
}
- 配置类
RabbitMQConfig
@Configuration
public class RabbitMQConfig {
// 声明队列,定义到Spring容器中
@Bean
public Queue directQueue(){
return new Queue("directQueue");
}
@Bean
public DirectExchange directExchange(){
return new DirectExchange("directExchange");
}
@Bean
public Binding binding(){
return new Binding("directQueue", Binding.DestinationType.QUEUE,"directExchange","directKey",null);
}
}
- 引导类
@SpringBootApplication
public class Mq07SpringBootSendApplication implements CommandLineRunner {
@Resource
private Send send;
public static void main(String[] args) {
SpringApplication.run(Mq07SpringBootSendApplication.class, args);
}
@Override
public void run(String... args) throws Exception {
send.directSend("SpringBoot send direct test message ~");
}
}
- 创建接收模块,添加模块依赖,配置
application.properties
文件,内容同上。 - 接收类
Receive.java
,配置类同上
@Component
public class Receive {
@Resource
private AmqpTemplate amqpTemplate;
// 用 receiveAndConvert() 只能接收一条消息,不能持续监听队列
public void directReceive() {
String info = (String) amqpTemplate.receiveAndConvert("directQueue");
System.out.println("接收到的消息为: " + info);
}
/**
* 当使用 @RabbitListener 注解标记一个方法时,Spring 框架会自动创建消息监听容器,并在接收到消息时调用被注解的方法
* @param message
*/
@RabbitListener(queues = "directQueue")
public void directReceiveListener(String message){
System.out.println("接收到的消息为: " + message);
}
}
- 测试
基于fanout的收发
- 创建发送模块,添加
Spring for RabbitMQ
依赖 application.properties
配置文件- 发送类
Send.java
@Component
public class Send {
// 支持一些基本的发送和接收
@Resource
private AmqpTemplate amqpTemplate;
public void fanoutSend(String message){
amqpTemplate.convertAndSend("fanoutExchange","",message);
}
}
- 配置类
RabbitMQConfig
@Configuration
public class RabbitMQConfig {
@Bean
public FanoutExchange fanoutExchange(){
return new FanoutExchange("fanoutExchange");
}
}
- 引导类
@SpringBootApplication
public class Mq07SpringBootSendApplication implements CommandLineRunner {
@Resource
private Send send;
public static void main(String[] args) {
SpringApplication.run(Mq07SpringBootSendApplication.class, args);
}
@Override
public void run(String... args) throws Exception {
send.fanoutSend("SpringBoot send fanout test message ~");
}
}
- 创建接收模块,添加模块依赖,配置
application.properties
文件,内容同上。 - 接收类
Receive.java
,配置类同上
@Component
public class Receive {
@Resource
private AmqpTemplate amqpTemplate;
@RabbitListener(bindings = @QueueBinding(value = @Queue,exchange = @Exchange(name = "fanoutExchange",type = "fanout")))
public void fanoutReceiveListener01(String message){
System.out.println("fanoutReceiveListener01 接收到的消息为: " + message);
}
}
基于topic的收发
- 创建发送模块,添加
Spring for RabbitMQ
依赖 application.properties
配置文件- 发送类
Send.java
@Component
public class Send {
// 支持一些基本的发送和接收
@Resource
private AmqpTemplate amqpTemplate;
public void fanoutSend(String message){
amqpTemplate.convertAndSend("topicExchange","",message);
}
}
- 配置类
RabbitMQConfig
@Configuration
public class RabbitMQConfig {
@Bean
public TopicExchange topicExchange(){
return new TopicExchange("topicExchange");
}
}
- 引导类
@SpringBootApplication
public class Mq07SpringBootSendApplication implements CommandLineRunner {
@Resource
private Send send;
public static void main(String[] args) {
SpringApplication.run(Mq07SpringBootSendApplication.class, args);
}
@Override
public void run(String... args) throws Exception {
send.fanoutSend("SpringBoot send fanout test message ~");
}
}
- 创建接收模块,添加模块依赖,配置
application.properties
文件,内容同上。 - 接收类
Receive.java
,配置类同上
@Component
public class Receive {
@Resource
private AmqpTemplate amqpTemplate;
@RabbitListener(bindings = @QueueBinding(value = @Queue,exchange = @Exchange(name = "topicExchange",type = "topic")))
public void fanoutReceiveListener01(String message){
System.out.println("fanoutReceiveListener01 接收到的消息为: " + message);
}
}