RabbitMQ 消息确认机制ACK
- ack机制保证的是broker和消费者之间的可靠性
- ack表示的是消费端收到消息后的确认方式,有三种确认方式
- 自动确认:
acknowledge="none"
(默认) - 手动确认:
acknowledge="manual"
- 根据异常情况确认:
acknowledge="auto"
(这种方式使用麻烦,不作讲解)
- 自动确认的解释
- 当消息一旦被 Consumer 接收到,则自动确认收到,并将相应消息从 RabbitMQ 的消息缓存中移除
- 手动确认的解释
- 在实际业务处理中,很可能消费端收到消息后业务处理出现异常,那么该消息就会丢失
- 如果设置了手动确认方式,则需要在业务处理成功后,调用
channel.basicAck()
手动签收;如果出现异常,则调用channel.basicNack()
方法,让 broker 自动重新发送消息给消费端
代码实现
- spring-rabbitmq-consumer.xml
配置文件中的监听器容器标签中定义消息的确认方式
<!--加载配置文件-->
<!--配置文件中定义了端口、用户名、密码等信息-->
<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.itheima.listener" />
<!--定义监听器容器-->
<!--acknowledge属性设置消息的确认方式,manual为手 动确认方式-->
<rabbit:listener-container connection-factory="connectionFactory" acknowledge="manual">
<!--创建的监听器的类名是AckListener-->
<rabbit:listener ref="ackListener" queue-names="test_queue_confirm"/>
</rabbit:listener-container>
- 创建引导类,保证消费者可以持续监听某队列
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:spring-rabbitmq-consumer.xml")
public class ConsumerTest {
@Test
public void test(){
while (true){
}
}
}
- 创建监听器AckListener.java
监听器类实现ChannelAwareMessageListener
接口,默认的自动签收是实现MessageListener
接口
@Component
public class AckListener implements ChannelAwareMessageListener {
//每读取一条消息就会执行一次此方法,message表示封装后的每次读取到的消息
@Override
public void onMessage(Message message, Channel channel) throws Exception {
// 得到消息的deliveryTag,作为参数传递到basicAck方法中
long deliveryTag = message.getMessageProperties().getDeliveryTag();
try {
// 对message操作,处理业务逻辑
int i = 3/0; //手动制造错误
// 如果没有错误,则执行此步,表示成功签收
// 手动签收,第一个参数表示消息的deliveryTag,第二个参数表示是否允许多条消息同时被签收
channel.basicAck(deliveryTag, true);
} catch (Exception e) {
//执行到此步说明出现了错误,拒绝签收,重新发送消息
/**
* 前两个参数和basicAck方法一致
* 第三个参数指的是requeue,如果设置为true,则消息重新回到queue,broker会重新发送该消息给消费端
* 第三个参数如果设置为false,则消息不会返回到原队列
*/
channel.basicNack(deliveryTag, true, true);
//由于消费者持续监听,且消息持续不断的重新发送,此段代码会一直循环执行,直到处理了错误
}
}
}