Return Listener用于处理一些不可路由的消息。
我们的消息生产者,通过指定一个Exchange和Routingkey,把消息送到某一个队列中,
然后我们的消费者监听队列,进行消息处理操作。
但是在某些情况下,如果我们在发送消息的时候,当前的exchange不存在或者指定的路由key路由不到,
这个时候我们需要监听这种不可达的消息,就要使用return listener。
package com.sy.rabbitmq.return_listener;
import com.rabbitmq.client.*;
import com.sy.rabbitmq.TestProperties;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
/**
* 消息返回机制 -return 生产端
*/
public class Producer {
public static void main(String[] args) throws IOException, TimeoutException {
// 创建ConnectionFactory
ConnectionFactory factory = new ConnectionFactory();
factory.setHost(TestProperties.getIp());
factory.setPort(TestProperties.getPort());
factory.setVirtualHost("/");
//2.获取Connection
Connection connection = factory.newConnection();
//3.获取channel
Channel channel = connection.createChannel();
String exchangeName = "test_confirm_exchange";
String routeKey = "save";
//5.发送一条消息
String msg = "Hello RabbitMQ send confirm message!";
//6.添加一个错误路由监听-防止消息未送达
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("-------------return无法路由消息--------------");
}
}
);
channel.basicPublish(exchangeName,routeKey,true,null,msg.getBytes());
}
}
package com.sy.rabbitmq.return_listener;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.QueueingConsumer;
import com.sy.rabbitmq.TestProperties;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
/**
* 消息返回机制 -return 消费端
*/
public class Comsumer {
public static void main(String[] args) throws IOException, TimeoutException, InterruptedException {
// 创建ConnectionFactory
ConnectionFactory factory = new ConnectionFactory();
factory.setHost(TestProperties.getIp());
factory.setPort(TestProperties.getPort());
factory.setVirtualHost("/");
//2.获取Connection
Connection connection = factory.newConnection();
//3.获取channel
Channel channel = connection.createChannel();
//4.通过channel声明一个exchange
String exchangeName = "test_confirm_exchange";
String routeKey = "confirm.#";
String queueName = "test_confirm_queue";
channel.exchangeDeclare(exchangeName,"topic",true);
//5.通过channel声明一个队列,绑定设置路由key
channel.queueDeclare(queueName,true,false,false,null);
channel.queueBind(queueName,exchangeName,routeKey);
//6.创建消费者
QueueingConsumer consumer = new QueueingConsumer(channel);
//7.绑定消费到队列上
channel.basicConsume(queueName,true,consumer);
while(true){
QueueingConsumer.Delivery delivery = consumer.nextDelivery();
String msg = new String(delivery.getBody());
System.out.println(msg);
}
}
}
正常情况下,如果消息经过交换器进入队列就可以完成消息的持久化,但如果消息在没有到达broker之前出现意外,那就造成消息丢失
如何实现Confirm确认消息?
第一步:在channel上开启确认模式:channel.confirmSelect()
第二步:在channel上添加监听:addConfirmListener,监听成功和失败的返回结果,
根据具体的结果对消息进行重新发送、或者记录日志等后续处理。
package com.sy.rabbitmq.confirm_listener;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.ConfirmListener;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import com.sy.rabbitmq.TestProperties;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
/**
* 消息确认模式 -confirm 生产端
*/
public class Producer {
public static void main(String[] args) throws IOException, TimeoutException {
// 创建ConnectionFactory
ConnectionFactory factory = new ConnectionFactory();
factory.setHost(TestProperties.getIp());
factory.setPort(TestProperties.getPort());
factory.setVirtualHost("/");
//2.获取Connection
Connection connection = factory.newConnection();
//3.获取channel
Channel channel = connection.createChannel();
//4.指定消息的投递模式: confirmSelect: 消息的确认模式
channel.confirmSelect();
String exchangeName = "test_confirm_exchange";
String routeKey = "confirm.save";
//5.发送一条消息
String msg = "Hello RabbitMQ send confirm message!";
channel.basicPublish(exchangeName,routeKey,null,msg.getBytes());
//6.添加一个确认监听
channel.addConfirmListener(
new ConfirmListener() {
@Override
public void handleAck(long deliveryTag, boolean multiple) throws IOException {
System.out.println("----------ack----------");
}
@Override
public void handleNack(long deliveryTag, boolean multiple) throws IOException {
System.out.println("----------no-ack----------");
}
}
);
}
}
package com.sy.rabbitmq.confirm_listener;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.QueueingConsumer;
import com.sy.rabbitmq.TestProperties;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
/**
* 消息确认模式 -confirm 消费端端
*/
public class Comsumer {
public static void main(String[] args) throws IOException, TimeoutException, InterruptedException {
// 创建ConnectionFactory
ConnectionFactory factory = new ConnectionFactory();
factory.setHost(TestProperties.getIp());
factory.setPort(TestProperties.getPort());
factory.setVirtualHost("/");
//2.获取Connection
Connection connection = factory.newConnection();
//3.获取channel
Channel channel = connection.createChannel();
//4.通过channel声明一个exchange
String exchangeName = "test_confirm_exchange";
String routeKey = "confirm.#";
String queueName = "test_confirm_queue";
channel.exchangeDeclare(exchangeName,"topic",true);
//5.通过channel声明一个队列,绑定设置路由key
channel.queueDeclare(queueName,true,false,false,null);
channel.queueBind(queueName,exchangeName,routeKey);
//6.创建消费者
QueueingConsumer consumer = new QueueingConsumer(channel);
//7.绑定消费到队列上
channel.basicConsume(queueName,true,consumer);
while(true){
QueueingConsumer.Delivery delivery = consumer.nextDelivery();
String msg = new String(delivery.getBody());
System.out.println(msg);
}
}
}
消费端使用循环消费队列中的消息,代码上不太美观可以采用:
package com.sy.rabbitmq.consumer;
import com.rabbitmq.client.*;
import com.sy.rabbitmq.TestProperties;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
/**
* 新消费方式 绑定对象
*/
public class Comsumer {
public static void main(String[] args) throws IOException, TimeoutException, InterruptedException {
// 创建ConnectionFactory
ConnectionFactory factory = new ConnectionFactory();
factory.setHost(TestProperties.getIp());
factory.setPort(TestProperties.getPort());
factory.setVirtualHost("/");
//2.获取Connection
Connection connection = factory.newConnection();
//3.获取channel
Channel channel = connection.createChannel();
//4.通过channel声明一个exchange
String exchangeName = "test_confirm_exchange";
String routeKey = "confirm.#";
String queueName = "test_confirm_queue";
channel.exchangeDeclare(exchangeName,"topic",true);
//5.通过channel声明一个队列,绑定设置路由key
channel.queueDeclare(queueName,true,false,false,null);
channel.queueBind(queueName,exchangeName,routeKey);
//---- 老版本编写方式 ----
//6.创建消费者
// QueueingConsumer consumer = new QueueingConsumer(channel);
//7.绑定消费到队列上
// channel.basicConsume(queueName,true,consumer);
// while(true){
// QueueingConsumer.Delivery delivery = consumer.nextDelivery();
// String msg = new String(delivery.getBody());
// System.out.println(msg);
// }
//---- 新方式直接绑定对象,每次消费过来会调用对象的handleDelivery ----
channel.basicConsume(queueName,true,new MyConsumer(channel));
}
/**
* 重写消费端
*/
public static class MyConsumer extends DefaultConsumer {
/**
* Constructs a new instance and records its association to the passed-in channel.
*
* @param channel the channel to which this consumer is attached
*/
public MyConsumer(Channel channel) {
super(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 {
System.out.println("-----------------新消费方式-----------------");
System.out.println("consumerTag"+consumerTag);
System.out.println("envelope"+envelope);
System.out.println("AMQP.BasicProperties"+properties);
System.out.println("body"+new String(body));
}
}
}