Java初识RabbitMQ一Return消息机制
什么是Return消息机制
Return消息机制用于处理一些不可路由的消息。正常情况下,消息生产端通过指定一个Exchange和RoutingKey,把消息路由到某一个队列中去,然后消费端监听队列,进行消费。但在某些情况下,如在发送消息的时候,当前的Exchange不存在或者指定的RoutingKey路由不到Queue,这个时候,如果我们需要监听这种不可达的消息, 就要使用Return消息机制(ReturnListener
)。
在基础API中有一个关键的配置项mandatory
:
- 如果为true,则监听器会接收到路由不可达的消息,然后进行后续处理。
- 如果为false,那么
broker
会自动删除该消息。
// mandatory 为 true
channel.basicPublish(exchange , routingKey , true , null , msg.getBytes());
生产端
我们这里使用的是默认交换机,它的路由规则可以看看下面这篇博客。
Java初识RabbitMQ一交换机(default exchange)
package com.kaven.rabbitmq.api.returnListener;
import com.rabbitmq.client.*;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
public class Producer {
// 自己服务器的IP
private static String ip = "IP";
// RabbitMQ启动的默认端口,也是应用程序进行连接RabbitMQ的端口
private static int port = 5672;
// RabbitMQ有一个 "/" 的虚拟主机
private static String virtualHost = "/";
// default exchange
private static String exchange = "";
// default exchange 的路由规则: routingKey(test) 将匹配同名的 queue(test)
private static String routingKey = "test";
private static String routingKeyError = "test_1";
public static void main(String[] args) throws IOException, TimeoutException {
// 1 创建ConnectionFactory
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.setHost(ip);
connectionFactory.setPort(port);
connectionFactory.setVirtualHost(virtualHost);
// 2 创建Connection
Connection connection = connectionFactory.newConnection();
// 3 创建Channel
Channel channel = connection.createChannel();
//4 添加ReturnListener
channel.addReturnListener(new ReturnListener() {
@Override
public void handleReturn(int replyCode, String replyText, String exchange, String routingKey,
AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println("---------- handle return ------------");
System.out.println("replyCode:" + replyCode);
System.out.println("replyText:" + replyText);
System.out.println("exchange:" + exchange);
System.out.println("routingKey:" + routingKey);
System.out.println("properties:" + properties);
System.out.println("body:" + new String(body));
}
});
// 5 发送消息
String msg = "RabbitMQ: return message";
String msgError = "RabbitMQ: error return message";
// mandatory 为 true
channel.basicPublish(exchange , routingKey , true , null , msg.getBytes());
channel.basicPublish(exchange , routingKeyError , true,null , msgError.getBytes());
}
}
消费端
package com.kaven.rabbitmq.api.returnListener;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.QueueingConsumer;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
public class Consumer {
// 自己服务器的IP
private static String ip = "IP";
// RabbitMQ启动的默认端口,也是应用程序进行连接RabbitMQ的端口
private static int port = 5672;
// RabbitMQ有一个 "/" 的虚拟主机
private static String virtualHost = "/";
// default exchange
private static String exchange = "";
// 队列名
private static String queueName = "test";
public static void main(String[] args) throws IOException, TimeoutException, InterruptedException {
// 1 创建ConnectionFactory
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.setHost(ip);
connectionFactory.setPort(port);
connectionFactory.setVirtualHost(virtualHost);
// 2 创建Connection
Connection connection = connectionFactory.newConnection();
// 3 创建Channel
Channel channel = connection.createChannel();
// 4 创建Queue
channel.queueDeclare(queueName , true , false , false , null);
// 5 创建消费者
QueueingConsumer consumer = new QueueingConsumer(channel);
channel.basicConsume(queueName , true , consumer);
// 6 接收消息
while (true){
QueueingConsumer.Delivery delivery = consumer.nextDelivery();
String msg = new String(delivery.getBody());
System.out.println(msg);
}
}
}
测试
根据生产端和消费端的代码可以知道,RoutingKey = "test_1"
的消息是路由不到对应的Queue,因为我们这里是没有定义名称为 test_1
的队列,从RabbitMQ Management
可以看到,目前已经定义的Queue。
启动生产端,从RabbitMQ Management
可以看到,名称为test
的队列有一条消息已经准备好了。
生产端输出:
---------- handle return ------------
replyCode:312
replyText:NO_ROUTE
exchange:
routingKey:test_1
properties:#contentHeader<basic>(content-type=null, content-encoding=null, headers=null, delivery-mode=null, priority=null, correlation-id=null, reply-to=null, expiration=null, message-id=null, timestamp=null, type=null, user-id=null, app-id=null, cluster-id=null)
body:RabbitMQ: error return message
显然结果是正确的。
启动消费端,输出如下:
RabbitMQ: return message
显然消费端已经收到消息了。
从RabbitMQ Management
可以看到,名称为test
的队列没有准备好的消息,因为已经被消费端接收了。
再来试一下mandatory
为 false
的情况,生产端需要修改代码,消费端不需要修改代码。
生产端:
package com.kaven.rabbitmq.api.returnListener;
import com.rabbitmq.client.*;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
public class Producer {
// 自己服务器的IP
private static String ip = "IP";
// RabbitMQ启动的默认端口,也是应用程序进行连接RabbitMQ的端口
private static int port = 5672;
// RabbitMQ有一个 "/" 的虚拟主机
private static String virtualHost = "/";
// default exchange
private static String exchange = "";
// default exchange 的路由规则: routingKey(test) 将匹配同名的 queue(test)
private static String routingKey = "test";
private static String routingKeyError = "test_1";
public static void main(String[] args) throws IOException, TimeoutException {
// 1 创建ConnectionFactory
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.setHost(ip);
connectionFactory.setPort(port);
connectionFactory.setVirtualHost(virtualHost);
// 2 创建Connection
Connection connection = connectionFactory.newConnection();
// 3 创建Channel
Channel channel = connection.createChannel();
//4 添加ReturnListener
channel.addReturnListener(new ReturnListener() {
@Override
public void handleReturn(int replyCode, String replyText, String exchange, String routingKey,
AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println("---------- handle return ------------");
System.out.println("replyCode:" + replyCode);
System.out.println("replyText:" + replyText);
System.out.println("exchange:" + exchange);
System.out.println("routingKey:" + routingKey);
System.out.println("properties:" + properties);
System.out.println("body:" + new String(body));
}
});
// 5 发送消息
String msg = "RabbitMQ: return message";
String msgError = "RabbitMQ: error return message";
// mandatory 为 false
channel.basicPublish(exchange , routingKeyError , false,null , msgError.getBytes());
}
}
启动生产端,生产端的ReturnListener
并没有被调用(没有输出),看看RabbitMQ Management
,也没有消息准备好,broker
会自动删除这条消息。