MQ的消费端,线上环境里都是用的手工方式进行ACK和NACK。
ACK当消息正常消费,给MQ的Broker一个应答,表示消息消费完成,业务处理正常
NACK当消费端消费消息,业务异常了,我们可以NACK,加上重回队列,消息会重新投递,如果多次重投后还是失败就进行补偿策略。
消费端的重回队列:没有处理成功的消息,执行NACK,并且设置消息重新返回到MQ,然后可以重新投递。(重回队列是放到队列尾部)
ack:channel.basicAck()
nack:channel.basicNack()
tips:只有nack时候才有重回队列,但是具体是否需要重回队列需要自己的业务来定。例如:你消息处理失败,做nack,但是只记录日志,不做重回队列操作也是可以的。
演示代码
生产端
package com.vivo.demo1.ackandrequeue;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
/**
* @author:luzaichun
* @Date:2020/12/24
* @Time:23:13
**/
public class Producer {
public static void main(String[] args) throws IOException, TimeoutException {
ConnectionFactory factory = new ConnectionFactory();
factory.setPort(5672);
factory.setHost("192.168.3.8");
factory.setVirtualHost("/");
Connection connection = factory.newConnection();
Channel channel = connection.createChannel();
channel.basicPublish("ack_exchange","topic.ack",false,null,"111".getBytes());
channel.basicPublish("ack_exchange","topic.ack",false,null,"222".getBytes());
}
}
消费端
package com.vivo.demo1.ackandrequeue;
import com.rabbitmq.client.*;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
/**
* @author:luzaichun
* @Date:2020/12/24
* @Time:23:16
**/
public class Consumer {
public static void main(String[] args) throws IOException, TimeoutException {
ConnectionFactory factory = new ConnectionFactory();
factory.setVirtualHost("/");
factory.setPort(5672);
factory.setHost("192.168.3.8");
Connection connection = factory.newConnection();
Channel channel = connection.createChannel();
channel.exchangeDeclare("ack_exchange", "topic", false, false, null);
channel.queueDeclare("ack_queue",false,false,false,null);
channel.queueBind("ack_queue","ack_exchange","topic.#");
channel.basicConsume("ack_queue",false,new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
super.handleDelivery(consumerTag, envelope, properties, body);
String msg = new String(body);
if ("111".equals(msg)){
System.out.println("消息:"+msg);
//ack业务处理成功,执行ack
channel.basicAck(envelope.getDeliveryTag(),false);
System.out.println("该消息业务完成,进行ack==========");
}else {
System.out.println("消息:"+msg);
//Nack时候,requeue设置为true表示重回队列,消息会重新投递
channel.basicNack(envelope.getDeliveryTag(),false,true);
System.out.println("该条消息业务处理失败,进行Nack==========");
}
}
});
}
}
结果:发现消息111只消费了一次,然后ack后就没有了。消息222因为做的nack操作并且重回队列,然后消息222就一直nack,一直重回队列重新投递重新消费。