开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第14天,点击查看活动详情
ack - acknowledge,在 RabbitMQ 中指代消费者收到消息后确认的行为,能够反映消费者是否接收到MQ的消息并成功消费。
确认方式
目前提供了三种确认方式:
- acknowledge="none":当消费者接收到消息后,自动给到 MQ 一个回执,没有后续消费者业务逻辑是否成功。
- acknowledge="manual":消费者接收到消息后,处理业务逻辑,通过调用代码的方式告诉MQ 消息是否消费成功,如果失败,MQ会再次发送一遍消息,做一些重试机制。
- acknowledge="auto":通过抛出异常的类型来做响应的处理(比如重发、确认等等)
如果设置了手动确认消息是否接收成功的方式,需要手动调用 channel.basicAck(),手动的签收。如果业务处理失败,手动调用channel.basicNack() 方法拒收,并让MQ重新发送该消息。
如果不做任何关于acknowledge的配置,默认就是自动确认签收的。
代码示例
pom文件
spring:
rabbitmq:
addresses: xx.xx.xx.xxx
username: xxx
password: xxxx
port: 5672
listener:
simple:
# 关键
acknowledge-mode: manual
retry:
enabled: true
# 重试次数
max-attempts: 5
# 重试最大间隔时间
max-interval: 10000
# 重试初始间隔时间
initial-interval: 2000
# 间隔时间乘子,间隔时间*乘子=下一次的间隔时间,最大不能超过设置的最大间隔时间
multiplier: 2
config
@Configuration
public class RabbitMQConfig {
public static final String TEST_ACK = "test.ack";
@Bean
public Queue getQueue(){
return new Queue(COUNT_STATISTICS_QUEUE);
}
}
生产者
@Order
@Component
@Slf4j
public class HelloSender {
@Autowired
private AmqpTemplate rabbitTemplate;
public void testAck() {
// //消费者接收到该消息,解析到true,就模拟调用channel.basicAck确认签收消息
// this.rabbitTemplate.convertAndSend(RabbitMQConfig.TEST_ACK, "test msg send [true]");
//消费者接收到该消息,解析到false,就模拟调用channel.basicNack,拒收消息,让MQ重发
rabbitTemplate.convertAndSend(RabbitMQConfig.TEST_ACK, "test msg send [false]");
}
}
消费者
@Component
public class AckListener implements ChannelAwareMessageListener {
@RabbitListener(queuesToDeclare = @Queue(value = RabbitMQConfig.TEST_ACK))
@Override
public void onMessage(Message message, Channel channel) throws Exception {
Thread.sleep(1000);
boolean tag = new String(message.getBody()).contains("true");
System.out.println("接收到msg:" + new String(message.getBody()));
//获取mes deliveryTag
long deliveryTag = message.getMessageProperties().getDeliveryTag();
try {
if (tag) {
System.out.println("业务处理成功");
//手动签收
/*
* deliveryTag:the tag from the received {@link com.rabbitmq.client.AMQP.Basic.GetOk} or {@link com.rabbitmq.client.AMQP.Basic.Deliver}
* multiple: ture确认本条消息以及之前没有确认的消息,false仅确认本条消息
*/
channel.basicAck(deliveryTag, false);
} else {
//模拟业务处理失败抛出异常
System.out.println("业务处理失败");
throw new IOException("业务处理失败");
}
} catch (IOException e) {
e.printStackTrace();
/*
* deliveryTag:the tag from the received {@link com.rabbitmq.client.AMQP.Basic.GetOk} or {@link com.rabbitmq.client.AMQP.Basic.Deliver}
* multiple: ture确认本条消息以及之前没有确认的消息,false仅确认本条消息
* requeue: true该条消息重新返回MQ queue,MQ broker将会重新发送该条消息
*/
channel.basicNack(deliveryTag, false, true);
//也可以使用channel.basicReject(deliveryTag, requeue),它只能拒收单条消息
//channel.basicReject(deliveryTag, true);
}
}
}
使用 queuesToDeclare 别使用 queues ,这样第一次可以自动创建队列,避免报错