Confirm 消息确认机制
消息的确认,是指生产者投递消息后,如果 Broker 收到消息,则会给生产者一个应答。生产者能接收应答,用来确定这条消息是否正常的发送到 Broker,这种方式是消息可靠性投递的核心保障。
MyProducer.java
public class MyProducer {
public static void main(String[] args) throws Exception {
// 创建连接工厂,并进行属性设置
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.setHost("192.168.0.125");
connectionFactory.setPort(5672);
connectionFactory.setVirtualHost("/");
// 获取连接
Connection connection = connectionFactory.newConnection();
// 创建信道
Channel channel = connection.createChannel();
// 指定消息的投递模式 --消息的确认模式
channel.confirmSelect();
String exchangeName = "exchange_confirm";
String routingKey = "test.confirm";
// 通过信道发送信息
String msg = "hello, rabbitmq, test confirm ";
channel.basicPublish(exchangeName, routingKey, null, msg.getBytes());
// 添加确认监听
channel.addConfirmListener(new ConfirmListener() {
@Override
public void handleAck(long deliveryTag, boolean multiple) throws IOException {
System.out.println("======handle Ack==========");
}
@Override
public void handleNack(long deliveryTag, boolean multiple) throws IOException {
System.out.println("======handle Nack==========");
}
});
System.out.println("生产端消息已发送==" + msg);
// 不关闭连接,要不然监听不到确认消息
}
}
MyConsumer.java
public class MyConsumer {
public static void main(String[] args) throws Exception {
// 创建连接工厂,并进行属性设置
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.setHost("192.168.0.125");
connectionFactory.setPort(5672);
connectionFactory.setVirtualHost("/");
// 获取连接
Connection connection = connectionFactory.newConnection();
// 创建信道
Channel channel = connection.createChannel();
// 指定消息的投递模式 --消息的确认模式
channel.confirmSelect();
String exchangeName = "exchange_confirm";
String routingKey = "*.confirm";
String queueName = "queue_confirm";
channel.exchangeDeclare(exchangeName, "topic", true);
channel.queueDeclare(queueName, true, false, false, null);
channel.queueBind(queueName, exchangeName, routingKey);
Consumer consumer = new DefaultConsumer(channel) {
@Override
public void handleDelivery(String consumerTag,
Envelope envelope,
AMQP.BasicProperties properties,
byte[] body) throws IOException {
String msg = new String(body, "UTF-8");
//TODO 自己的业务
//交换机
String exchange = envelope.getExchange();
//路由key
String routingKey = envelope.getRoutingKey();
//消息id
long deliveryTag = envelope.getDeliveryTag();
//消息内容
System.err.println("消费端收到 message : " + msg);
}
};
channel.basicConsume(queueName, true, consumer);
}
}
启动 MQ 服务,确保交换机和队列已经声明后,执行生产端代码,输出如下:
说明 Broker 端已成功收到我们发送的消息。
什么情况下触发 noack ?
磁盘写满;MQ 出现异常;queue 容量到达上限等
Return 消息机制
Return 消息机制用于处理一个不可路由的消息。在某些情况下,如果我们在发送消息的时候,当前的 exchange 不存在或者指定路由 key 路由不到,这个时候如果我们需要监听这种不可达的消息,就要使用 Return 消息机制了。
以下为生产端和消费端的代码片段
----生产端代码片段
String exchangeName = "exchange_return";
String routingKey = "test.return";
String routingKeyError = "test.error";
// 通过信道发送信息
String msg = "hello, rabbitmq, test return ";
// return 监听
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("====returnListener print=====");
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));
}
});
// mandatory 为 true时,如果消息路由不到,不会删除消息,监听器可接受到该消息,可做后续的处理;
// mandatory 默认为 false ,如果消息路由不到, Broke 端会自动删除该消息。
// channel.basicPublish(exchange, routingKey, mandatory, props, body);
// channel.basicPublish(exchangeName, routingKey, true, null, msg.getBytes());
channel.basicPublish(exchangeName, routingKeyError, true, null, msg.getBytes());
System.out.println("生产端消息已发送==" + msg);
消费端代码片段
String exchangeName = "exchange_return";
String routingKey = "*.return";
String queueName = "queue_return";
channel.exchangeDeclare(exchangeName, "topic", true);
channel.queueDeclare(queueName, true, false, false, null);
channel.queueBind(queueName, exchangeName, routingKey);
Consumer consumer = new DefaultConsumer(channel) {
@Override
public void handleDelivery(String consumerTag,
Envelope envelope,
AMQP.BasicProperties properties,
byte[] body) throws IOException {
String msg = new String(body, "UTF-8");
//TODO 自己的业务
//交换机
String exchange = envelope.getExchange();
//路由key
String routingKey = envelope.getRoutingKey();
//消息id
long deliveryTag = envelope.getDeliveryTag();
//消息内容
System.err.println("消费端收到 message : " + msg);
}
};
channel.basicConsume(queueName, true, consumer);
mandatory 为 true时,如果消息路由不到,不会删除消息,监听器可接收到该消息,可做后续的处理;
mandatory 默认为 false ,如果消息路由不到, Broke 端会自动删除该消息,监听器将接收不到该消息。
当生产端使用正确的 routingKey 时,生产端发送的消息能路由到指定队列,消息的收发正常;
当生产端使用不正确的 routingKeyError 时,路由 key 路由不到指定的队列,Return 监听器可以监听到该条消息,可以后续处理该消息。