RabbitMQ的工作模式
在发送或者接收消息后我们可以在RabbitMQ的控制面板中去查看其发生的变化
对于RabbitMQ的不同工作模式,其本质上是基于不同类型的交换机(Exchange)和不同的绑定(Binding)规则来实现的
以下所有示例都需要导入此依赖,这些示例是使用了原生API,可以更好的帮助我们理解RabbitMQ的工作模式,在实际开发中仍然会使用Spring系列的框架对其进行整合.
我只在第一个示例中给出较为详细的代码,后续工作模式示例只给出核心代码
<dependency>
<groupId>com.rabbitmq</groupId>
<artifactId>amqp-client</artifactId>
</dependency>
简单模式
使用的交换机:默认交换机(“”,Direct 类型)
特点:
生产者直接发送消息到队列(实际上是发送到默认交换机,路由键 routing_key 设为队列名)
消费者直接从队列消费
/**
* 简单模式 - 生产者
* 特点:一个生产者发送消息到一个队列,一个消费者从该队列接收消息
*/
public class SimpleProducer {
// 定义队列名称
private final static String QUEUE_NAME = "hello";
public static void main(String[] args) throws Exception {
// 创建连接工厂
ConnectionFactory factory = new ConnectionFactory();
// 设置RabbitMQ服务器地址
factory.setHost("localhost");
// 设置RabbitMQ服务器端口号
factory.setPort(5672);
// 设置虚拟主机地址
factory.setVirtualHost("/");
// 设置RabbitMQ服务器用户名和密码
factory.setUsername("guest");
factory.setPassword("guest");
// try-with-resources确保资源自动关闭
try (Connection connection = factory.newConnection(); // 创建连接
Channel channel = connection.createChannel()) { // 创建通道
// 声明队列,参数说明:
// 1. 队列名称
// 2. 是否持久化(false表示不持久化)
// 3. 是否独占队列(false表示不独占)
// 4. 是否自动删除(false表示不自动删除)
// 5. 其他参数(null表示无其他参数)
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
// 准备发送的消息
String message = "Hello World!";
// 发布消息,参数说明:
// 1. 交换机名称(""表示默认交换机)
// 2. 路由键(这里是队列名称)
// 3. 消息属性(null表示无特殊属性)
// 4. 消息体(字节数组)
channel.basicPublish("", QUEUE_NAME, null, message.getBytes());
System.out.println(" [x] Sent '" + message + "'");
}
}
}
/**
* 简单模式 - 消费者
*/
public class SimpleConsumer {
private final static String QUEUE_NAME = "hello";
public static void main(String[] args) throws Exception {
// 创建连接工厂
ConnectionFactory factory = new ConnectionFactory();
// 设置RabbitMQ服务器地址
factory.setHost("localhost");
// 设置RabbitMQ服务器端口号
factory.setPort(5672);
// 设置虚拟主机地址
factory.setVirtualHost("/");
// 设置RabbitMQ服务器用户名和密码
factory.setUsername("guest");
factory.setPassword("guest");
// 创建连接和通道
Connection connection = factory.newConnection();
Channel channel = connection.createChannel();
// 声明队列(必须与生产者声明的队列一致)
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
System.out.println(" [*] Waiting for messages...");
// 定义消息接收回调
DeliverCallback deliverCallback = (consumerTag, delivery) -> {
// 将消息体转换为字符串
String message = new String(delivery.getBody(), "UTF-8");
System.out.println(" [x] Received '" + message + "'");
};
// 开始消费消息,参数说明:
// 1. 队列名称
// 2. 是否自动确认(true表示自动确认)
// 3. 消息接收回调
// 4. 消费者取消回调(这里为空实现)
channel.basicConsume(QUEUE_NAME, true, deliverCallback, consumerTag -> {});
}
}
工作队列模式
使用的交换机:默认交换机(“”,Direct 类型)
特点:
多个消费者共享同一个队列,RabbitMQ 以轮询(Round-Robin)方式分发消息
适合任务分发场景
// 生产者
channel.queueDeclare("task_queue", true, false, false, null); // 持久化队列
String message = "New task...";
channel.basicPublish("", "task_queue",
MessageProperties.PERSISTENT_TEXT_PLAIN, // 持久化消息
message.getBytes());
// 消费者(Worker)
DeliverCallback deliverCallback = (consumerTag, delivery) -> {
String message = new String(delivery.getBody(), "UTF-8");
System.out.println(" [x] Received '" + message + "'");
// 模拟工作耗时
try { Thread.sleep(1000); } catch (InterruptedException e) {}
System.out.println(" [x] Done");
channel.basicAck(delivery.getEnvelope().getDeliveryTag(), false);
};
channel.basicConsume("task_queue", false, deliverCallback, consumerTag -> {});
发布/订阅模式
使用的交换机:Fanout 交换机(扇出交换机)
特点:
生产者发送的消息会被广播到所有绑定的队列
每个消费者都有自己的独立队列
// 生产者
channel.exchangeDeclare("logs", "fanout"); // 声明fanout交换机
String message = "Broadcast message!";
channel.basicPublish("logs", "", null, message.getBytes()); // 路由键为空
// 消费者(每个消费者有自己的临时队列)
String queueName = channel.queueDeclare().getQueue();
channel.queueBind(queueName, "logs", ""); // 绑定到fanout交换机
路由模式
使用的交换机:Direct 交换机
特点:
生产者指定 routing_key,交换机根据 routing_key 将消息路由到匹配的队列
适用于精确匹配的消息分发(如 error、info 日志分类)
// 生产者
channel.exchangeDeclare("direct_logs", "direct"); // 声明direct交换机
String severity = "error"; // 路由键
String message = "Error log!";
channel.basicPublish("direct_logs", severity, null, message.getBytes());
// 消费者(只接收error级别的日志)
String queueName = channel.queueDeclare().getQueue();
channel.queueBind(queueName, "direct_logs", "error"); // 绑定指定路由键
主题模式
使用的交换机:Topic 交换机
特点:
支持通配符匹配 *(单个单词)和 #(多个单词)
适用于更灵活的路由规则(如 logs.error.*、orders.#)
// 生产者
channel.exchangeDeclare("topic_logs", "topic"); // 声明topic交换机
String routingKey = "logs.error";
String message = "Topic message!";
channel.basicPublish("topic_logs", routingKey, null, message.getBytes());
// 消费者(使用通配符绑定)
String queueName = channel.queueDeclare().getQueue();
channel.queueBind(queueName, "topic_logs", "logs.*"); // 匹配logs.开头的路由键
RPC模式
使用的交换机:默认交换机(Direct)或自定义交换机
特点
客户端发送请求消息
服务端处理请求并返回响应
使用回调队列和关联ID
// 客户端(发送RPC请求)
String callbackQueue = channel.queueDeclare().getQueue(); // 回调队列
AMQP.BasicProperties props = new AMQP.BasicProperties
.Builder()
.correlationId(UUID.randomUUID().toString())
.replyTo(callbackQueue)
.build();
channel.basicPublish("", "rpc_queue", props, "RPC request".getBytes());
// 服务端(处理RPC请求)
DeliverCallback deliverCallback = (consumerTag, delivery) -> {
AMQP.BasicProperties replyProps = new AMQP.BasicProperties
.Builder()
.correlationId(delivery.getProperties().getCorrelationId())
.build();
String response = processRpcRequest(new String(delivery.getBody(), "UTF-8"));
channel.basicPublish("", delivery.getProperties().getReplyTo(), replyProps, response.getBytes());
channel.basicAck(delivery.getEnvelope().getDeliveryTag(), false);
};
channel.basicConsume("rpc_queue", false, deliverCallback, consumerTag -> {});