当消息经过交换器被路由之后,在投递到队列的过程中,发生错误,就会触发发送方确认机制,返回Nack给生产者
一、一般确认
1.1.创建生产者ProducerConfirm,设置发送方确认模式
package com.rabbit.fisher.producerconfirm;
import com.rabbitmq.client.*;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
/**
*生产者——发送方确认模式--一般确认
*/
public class ProducerConfirm
{
public final static String EXCHANGE_NAME="producer_confirm";
private final static String ROUTE_KEY = "rabbit";
public static void main( String[] args ) throws IOException, TimeoutException, InterruptedException {
//创建链接、连接到MQ
ConnectionFactory connectionFactory = new ConnectionFactory();
//设置连接工厂的连接地址(默认端口5672)
connectionFactory.setHost("192.168.42.111");
//设置虚拟主机
connectionFactory.setVirtualHost("fisher");
//设置用户名
connectionFactory.setUsername("fisher");
//设置密码
connectionFactory.setPassword("123456");
//创建连接
Connection connection = connectionFactory.newConnection();
//创建信道
Channel channel = connection.createChannel();
//在信道中设置交换器(这里选择直接交换器direct)
channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.DIRECT);
//启动发送方确认模式
channel.confirmSelect();
//申明路由键及消息体
for (int i = 0; i < 2; i++) {
String msg = "Hello World" + (i+1);
//发布消息(设置交换器、路由键、失败通知、参数、消息内容)
channel.basicPublish(EXCHANGE_NAME, ROUTE_KEY, true, null, msg.getBytes());
System.out.println("Send:" + ROUTE_KEY + ";" + msg);
//确认是否成功
if (channel.waitForConfirms()) {
System.out.println("send success");
}else {
System.out.println("send failure");
}
}
//关闭信道
channel.close();
//关闭连接
connection.close();
}
}
1.2.创建消费者ProducerConfirmConsumer
package com.rabbit.fisher.producerconfirm;
import com.rabbitmq.client.*;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
/**
*消费者——发送方确认模式
*/
public class ProducerConfirmConsumer
{
public static void main( String[] args ) throws IOException, TimeoutException {
//创建链接、连接到MQ
ConnectionFactory connectionFactory = new ConnectionFactory();
//设置连接工厂的连接地址(默认端口5672)
connectionFactory.setHost("192.168.42.111");
//设置虚拟主机
connectionFactory.setVirtualHost("fisher");
//设置用户名
connectionFactory.setUsername("fisher");
//设置密码
connectionFactory.setPassword("123456");
//创建连接
Connection connection = connectionFactory.newConnection();
//创建信道
Channel channel = connection.createChannel();
//在信道中设置交换器(这里选择直接交换器direct)
channel.exchangeDeclare(ProducerConfirm.EXCHANGE_NAME, BuiltinExchangeType.DIRECT);
//申明队列
String queueName = channel.queueDeclare().getQueue();
//绑定路由键(rabbit)
String routeKey = "rabbit";
channel.queueBind(queueName, ProducerConfirm.EXCHANGE_NAME, routeKey);
System.out.println("waiting for message ......");
//申明一个消费者
final Consumer consumer = new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
String message = new String(body, "UTF-8");
System.out.println("Receive[" + envelope.getRoutingKey() + "]" + message);
}
};
channel.basicConsume(queueName, true, consumer);
}
}
1.3.先启动消费者
1.4.再启动生产者
1.5.查看生产者打印,确认成功
Send:rabbit;Hello World1
send success
Send:rabbit;Hello World2
send success
二、批量确认
2.1创建生产者ProducerBatchConfirm,设置批量确认
package com.rabbit.fisher.producerconfirm;
import com.rabbitmq.client.BuiltinExchangeType;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
/**
*生产者——发送方确认模式--批量确认
*/
public class ProducerBatchConfirm
{
public final static String EXCHANGE_NAME="producer_wait_confirm";
private final static String ROUTE_KEY = "rabbit";
public static void main( String[] args ) throws IOException, TimeoutException, InterruptedException {
//创建链接、连接到MQ
ConnectionFactory connectionFactory = new ConnectionFactory();
//设置连接工厂的连接地址(默认端口5672)
connectionFactory.setHost("192.168.42.111");
//设置虚拟主机
connectionFactory.setVirtualHost("fisher");
//设置用户名
connectionFactory.setUsername("fisher");
//设置密码
connectionFactory.setPassword("123456");
//创建连接
Connection connection = connectionFactory.newConnection();
//创建信道
Channel channel = connection.createChannel();
//在信道中设置交换器(这里选择直接交换器direct)
channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.DIRECT);
//启动发送方确认模式
channel.confirmSelect();
//申明路由键及消息体
for (int i = 0; i < 6; i++) {
String msg = "Hello World" + (i+1);
//发布消息(设置交换器、路由键、失败通知、参数、消息内容)
channel.basicPublish(EXCHANGE_NAME, ROUTE_KEY, true, null, msg.getBytes());
System.out.println("Send:" + ROUTE_KEY + ";" + msg);
}
//启动发送方确认(批量确认)
channel.waitForConfirmsOrDie();
//关闭信道
channel.close();
//关闭连接
connection.close();
}
}
2.2创建消费者ProducerBatchConfirmConsumer
package com.rabbit.fisher.producerconfirm;
import com.rabbitmq.client.*;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
/**
*消费者——发送方确认模式
*/
public class ProducerBatchConfirmConsumer
{
public static void main( String[] args ) throws IOException, TimeoutException {
//创建链接、连接到MQ
ConnectionFactory connectionFactory = new ConnectionFactory();
//设置连接工厂的连接地址(默认端口5672)
connectionFactory.setHost("192.168.42.111");
//设置虚拟主机
connectionFactory.setVirtualHost("fisher");
//设置用户名
connectionFactory.setUsername("fisher");
//设置密码
connectionFactory.setPassword("123456");
//创建连接
Connection connection = connectionFactory.newConnection();
//创建信道
Channel channel = connection.createChannel();
//在信道中设置交换器(这里选择直接交换器direct)
channel.exchangeDeclare(ProducerBatchConfirm.EXCHANGE_NAME, BuiltinExchangeType.DIRECT);
//申明队列
String queueName = channel.queueDeclare().getQueue();
//绑定路由键(rabbit)
String routeKey = "rabbit";
channel.queueBind(queueName, ProducerBatchConfirm.EXCHANGE_NAME, routeKey);
System.out.println("waiting for message ......");
//申明一个消费者
final Consumer consumer = new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
String message = new String(body, "UTF-8");
System.out.println("Receive[" + envelope.getRoutingKey() + "]" + message);
}
};
channel.basicConsume(queueName, true, consumer);
}
}
2.3先启动消费者
2.4再启动生产者
2.5查看消费者打印
三、异步监听确认
3.1创建生产者ProducerAsyncConfirm
异步确认不能关闭信道与连接
使用2个路由键,调用失败通知(不可路由rocket时),方便打印ack
package com.rabbit.fisher.producerconfirm;
import com.rabbitmq.client.*;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
/**
*生产者——发送方确认模式--异步监听确认
*/
public class ProducerAsyncConfirm
{
public final static String EXCHANGE_NAME="producer_async_confirm";
public static void main( String[] args ) throws IOException, TimeoutException, InterruptedException {
//创建链接、连接到MQ
ConnectionFactory connectionFactory = new ConnectionFactory();
//设置连接工厂的连接地址(默认端口5672)
connectionFactory.setHost("192.168.42.111");
//设置虚拟主机
connectionFactory.setVirtualHost("fisher");
//设置用户名
connectionFactory.setUsername("fisher");
//设置密码
connectionFactory.setPassword("123456");
//创建连接
Connection connection = connectionFactory.newConnection();
//创建信道
Channel channel = connection.createChannel();
//在信道中设置交换器(这里选择直接交换器direct)
channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.DIRECT);
//启动发送方确认模式
channel.confirmSelect();
//添加发送者确认监听器
channel.addConfirmListener(new ConfirmListener() {
//成功,deliveryTag表示id,multiple表示是否批量(true是批量,false是单条),当发送到一定数量时,multiple会变为true,批量确认
@Override
public void handleAck(long deliveryTag, boolean multiple) throws IOException {
System.out.println("send_ACK:" + deliveryTag + ",multiple:" + multiple);
}
//失败
@Override
public void handleNack(long deliveryTag, boolean multiple) throws IOException {
System.out.println("send_NACK:" + deliveryTag + ",multiple:" + multiple);
}
});
//添加失败通知
channel.addReturnListener(new ReturnListener() {
@Override
public void handleReturn(int replycode, String replyText, String exchange, String routeKey, AMQP.BasicProperties basicProperties, byte[] bytes) throws IOException {
String msg = new String(bytes);
System.out.println("路由失败:" + routeKey + "," + msg);
}
});
String[] routeKeys = {"rabbit", "rocket"};
for (int i = 0; i < 10; i++) {
String route = routeKeys[i % 2];
String msg = "Hello World" + (i+1);
//发布消息(设置交换器、路由键、失败通知、参数、消息内容)
channel.basicPublish(EXCHANGE_NAME, route, true, MessageProperties.PERSISTENT_BASIC, msg.getBytes());
System.out.println("Send:" + route + ";" + msg);
}
//关闭信道
// channel.close();
//关闭连接
// connection.close();
}
}
3.2创建消费者ProducerAsyncConfirmConsumer
package com.rabbit.fisher.producerconfirm;
import com.rabbitmq.client.*;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
/**
*消费者——发送方确认模式
*/
public class ProducerAsyncConfirmConsumer
{
public static void main( String[] args ) throws IOException, TimeoutException {
//创建链接、连接到MQ
ConnectionFactory connectionFactory = new ConnectionFactory();
//设置连接工厂的连接地址(默认端口5672)
connectionFactory.setHost("192.168.42.111");
//设置虚拟主机
connectionFactory.setVirtualHost("fisher");
//设置用户名
connectionFactory.setUsername("fisher");
//设置密码
connectionFactory.setPassword("123456");
//创建连接
Connection connection = connectionFactory.newConnection();
//创建信道
Channel channel = connection.createChannel();
//在信道中设置交换器(这里选择直接交换器direct)
channel.exchangeDeclare(ProducerAsyncConfirm.EXCHANGE_NAME, BuiltinExchangeType.DIRECT);
//申明队列
String queueName = channel.queueDeclare().getQueue();
//绑定路由键(rabbit)
String routeKey = "rabbit";
channel.queueBind(queueName, ProducerAsyncConfirm.EXCHANGE_NAME, routeKey);
System.out.println("waiting for message ......");
//申明一个消费者
final Consumer consumer = new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
String message = new String(body, "UTF-8");
System.out.println("Receive[" + envelope.getRoutingKey() + "]" + message);
}
};
channel.basicConsume(queueName, true, consumer);
}
}
3.3先启动消费者
3.4再启动生产者,multiple表示是否批量(true是批量,false是单条),当发送到一定数量时,multiple会变为true,表示批量确认
Send:rabbit;Hello World1
Send:rocket;Hello World2
Send:rabbit;Hello World3
Send:rocket;Hello World4
Send:rabbit;Hello World5
Send:rocket;Hello World6
Send:rabbit;Hello World7
Send:rocket;Hello World8
Send:rabbit;Hello World9
Send:rocket;Hello World10
路由失败:rocket,Hello World2
send_ACK:2,multiple:false
send_ACK:3,multiple:true
路由失败:rocket,Hello World4
send_ACK:4,multiple:false
路由失败:rocket,Hello World6
send_ACK:6,multiple:false
路由失败:rocket,Hello World8
send_ACK:8,multiple:false
路由失败:rocket,Hello World10
send_ACK:10,multiple:false
send_ACK:9,multiple:true
3.5查看消费者打印,只消费了绑定rabbit路由键的消息
waiting for message ......
Receive[rabbit]Hello World1
Receive[rabbit]Hello World3
Receive[rabbit]Hello World5
Receive[rabbit]Hello World7
Receive[rabbit]Hello World9