ack——acknowledge(vt. 承认;答谢;报偿;告知已收到;确认的意思),在RabbitMQ中指代的是消费者收到消息后确认的一种行为,关注点在于消费者能否实际接收到MQ发送的消息。
其提供了三种确认方式:
自动确认acknowledge=“none”:当消费者接收到消息的时候,就会自动给到RabbitMQ一个回执,告诉MQ我已经收到消息了,不在乎消费者接收到消息之后业务处理的成功与否。
手动确认acknowledge=“manual”:当消费者收到消息后,不会立刻告诉RabbitMQ已经收到消息了,而是等待业务处理成功后,通过调用代码的方式手动向MQ确认消息已经收到。当业务处理失败,就可以做一些重试机制,甚至让MQ重新向消费者发送消息都是可以的。
根据异常情况确认acknowledge=“auto”:该方式是通过抛出异常的类型,来做响应的处理(如重发、确认等)这种方式比较麻烦。
当消息一旦被消费者接收到,会立刻自动向MQ确认接收,并将响应的message从RabbitMQ消息缓存中移除,但是在实际的业务处理中,会出现消息收到了,但是业务处理出现异常的情况,在自动确认的模式下,该条业务处理失败的message就相当于被丢弃了。如果设置了手动确认,则需要在业务处理完成之后,手动调用channel.basicAck(),手动的签收,如果业务处理失败,则手动调用channel.basicNack()方法拒收,并让MQ重新发送该消息。
如果不做任何关于acknowledge的配置,默认就是自动确认签收的。
1、消费者配置:
server:
port: 81
spring:
rabbitmq:
host: www.996.com
port: 5672
username: rabbitadmin
password: rabbitadmin
virtual-host: /
listener:
simple:
acknowledge-mode: manual #手动
2、消费者接口方法:
/**
* 测试 ACK
* @param message
*/
public void receiveMessage(String message);
3、消费者接口方法实现:
/**
* 测试 ACK
* @param message
*/
@Override
@RabbitListener(queues = {RabbitMqConfig.DIRECT_QUEUE})
public void receiveMessage(String message) {
System.out.println("接收到的MQ消息:"+message);
//处理业务
System.out.println("处理业务");
}
4、生产者发送队列消息
/**
* convertAndSend(String exchange, String routingKey, Object object),c
*
* @param message
* @return
*/
@Override
public void sendMessage(String message) {
CorrelationData correlationData = new CorrelationData("1");
rabbitTemplate.convertAndSend(RabbitMqConfig.DIRECT_EXCHANGE,RabbitMqConfig.DIRECT_ROUTINGKEY,message,correlationData);
}
5、队列
6、消费者接收队列消息成功,但队列中的消息并未删除,只是状态变更为未确认状态
7、更改消息接收方法,进行手动ACK确认
/**
* 测试 ACK
* @param message
*/
@Override
@RabbitListener(queues = {RabbitMqConfig.DIRECT_QUEUE})
public void receiveMessage(String message, Channel channel,@Header(AmqpHeaders.DELIVERY_TAG) long deliveryTag) {
try {
System.out.println("接收到的MQ消息:"+message);
//处理业务
System.out.println("处理业务");
//手动Ack
/**
* 手动Ack参数说明
* basicAck(long deliveryTag, boolean multiple)
* deliveryTag:批量处理的标号,举例:这个队列现在有5条消息要消费,那么这批数据会标号从1-5递增,5的时候就会手动Ack multiple:是否批量处理
*
*/
System.out.println("deliveryTag:" + deliveryTag);
channel.basicAck(deliveryTag,true);
}catch (Exception e){
e.getMessage();
}
}
8、消费者接收队列消息并手动ACK确认
9、若业务中处理异常是可以拒签的,模拟业务
/**
* 测试 ACK
* @param message
*/
@Override
@RabbitListener(queues = {RabbitMqConfig.DIRECT_QUEUE})
public void receiveMessage(String message, Channel channel,@Header(AmqpHeaders.DELIVERY_TAG) long deliveryTag) {
try {
System.out.println("接收到的MQ消息:"+message);
//处理业务
System.out.println("处理业务"+1/0);
//手动Ack
/**
* 手动Ack参数说明
* basicAck(long deliveryTag, boolean multiple)
* deliveryTag:批量处理的标号,举例:这个队列现在有5条消息要消费,那么这批数据会标号从1-5递增,5的时候就会手动Ack multiple:是否批量处理
*
*/
System.out.println("deliveryTag:" + deliveryTag);
channel.basicAck(deliveryTag,true);
}catch (Exception e){
e.printStackTrace();
/**
* basicNack(long deliveryTag, boolean multiple, boolean requeue)
* requeue:是否送回队列
*/
try {
channel.basicNack(deliveryTag,false,true);
} catch (IOException ioException) {
ioException.printStackTrace();
}
}
}
10、测试,异常后消息会被送回队列再次被监听到继续消费,重试消费默认3次后便不再消费