概述
ack指Acknowledge,确认。表示消费端收到消息后的确认方式。
有三种确认方式:
- 自动确认: acknowledge= “none”
- 手动确认: acknowledge= “manual”
- 根据异常情况确认: acknowledge= “auto”,(这种方式使用麻烦,并且不常用)
自动确认是指
,当消息一旦被Consumer接收到,则自动确认收到,并将相应message从RabbitMQ的消息缓存中移除
。但是在实际业务处理中,很可能消息接收到,业务处理出现异常,那么该消息就会丢失
。
如果设置了手动确认方式,则需要在业务处理成功后,调用channel.basicAck(), 手动签收
,如果出现异常,则调用channel.basicNack0方法,让其自动重新发送消息
。
代码测试
修改配置文件
spring-rabbitmq-consumer.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:rabbit="http://www.springframework.org/schema/rabbit"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/rabbit
http://www.springframework.org/schema/rabbit/spring-rabbit.xsd">
<!--加载配置文件-->
<context:property-placeholder location="classpath:rabbitmq.properties"/>
<!-- 定义rabbitmq connectionFactory -->
<rabbit:connection-factory id="connectionFactory" host="${rabbitmq.host}"
port="${rabbitmq.port}"
username="${rabbitmq.username}"
password="${rabbitmq.password}"
virtual-host="${rabbitmq.virtual-host}"/>
<!-- 定义包扫描-->
<context:component-scan base-package="com.yy.listener" />
<!--定义监听器容器,加载这些监听类-->
<rabbit:listener-container connection-factory="connectionFactory" acknowledge="manual" >
<rabbit:listener ref="ackListener" queue-names="test_queue_confirm"></rabbit:listener>
</rabbit:listener-container>
</beans>
在监听类中去完成手动签收的功能
AckListener
package com.yy.listener;
import com.rabbitmq.client.Channel;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageListener;
import org.springframework.amqp.rabbit.listener.api.ChannelAwareMessageListener;
import org.springframework.stereotype.Component;
import java.io.IOException;
/**
* Consumer ACK机制:
* 1. 设置手动签收。acknowledge="manual",默认是自动签收
* 2. 让监听器类实现ChannelAwareMessageListener接口
* 3. 如果消息成功处理,则调用channel的 basicAck()签收
* 4. 如果消息处理失败,则调用channel的basicNack()拒绝签收,broker重新发送给consumer
*
*
*/
@Component
public class AckListener implements ChannelAwareMessageListener {
@Override
public void onMessage(Message message, Channel channel) throws Exception {
//当前收到消息的tag标签
long deliveryTag = message.getMessageProperties().getDeliveryTag();
try {
//1.接收转换消息
System.out.println(new String(message.getBody()));
//2. 处理业务逻辑
System.out.println("处理业务逻辑...");
int i = 3/0;//出现错误,则broker一直会重新发送该消息给消费端
//3. 手动签收
/*
第一个参数:当前收到消息的tag标签
第二个参数:设置为true代表允许所有的消息被签收
*/
channel.basicAck(deliveryTag,true);
} catch (Exception e) {
//e.printStackTrace();
//4.拒绝签收
/*
第三个参数:requeue:重回队列。
如果设置为true,则消息重新回到queue,broker会重新发送该消息给消费端
*/
channel.basicNack(deliveryTag,true,true);
}
}
}
总结
在rabbit:listener-container标签中设置acknowledge属性,设置ack方式 none:自动确认,manual: 手动确认
如果在消费端没有出现异常,则调用channel.basicAck(deliveryTag,false);方法确认签收消息
如果出现异常,则在catch中调用basicNack或basicReject,拒绝消息,让MQ重新发送消息。