一、前言
-
ack指Acknowledge,确认。 表示消费端收到消息后的确认方式。
-
有三种确认方式:
- 自动确认:acknowledge=“none”
- 手动确认:acknowledge=“manual”
- 根据异常情况确认:acknowledge=“auto”,(这种方式使用麻烦,不作讲解)
-
其中自动确认是指,当消息一旦被Consumer接收到,则自动确认收到,并将相应 message 从 RabbitMQ 的消息缓存中移除。但是在实际业务处理中,很可能消息接收到,业务处理出现异常,那么该消息就会丢失。如果设置了手动确认方式,则需要在业务处理成功后,调用channel.basicAck(),手动签收,如果出现异常,则调用channel.basicNack()方法,让其自动重新发送消息。
二、添加配置
-
修改配置文件application.yml
spring: rabbitmq: host: 127.0.0.1 port: 5672 username: nov17 password: nov17 virtual-host: testhost publisher-confirm-type: correlated #支持发布确认 publisher-returns: true #支持发布返回 listener: simple: acknowledge-mode: manual #acknowledgeMode设置为手动模式(NONE,MANUAL,AUTO) direct: acknowledge-mode: manual #acknowledgeMode设置为手动模式
三、生产者代码
-
创建RabbitMQ队列配置类com.example.rabbitmq_producer.config;
@Configuration public class RabbitMQConfig { //交换机名称 public static final String ITEM_EXCHANGE = "item_exchange"; //队列名称 public static final String ITEM_QUEUE = "item_queue"; //声明交换机 @Bean("itemExchange") public Exchange itemExchange(){ return ExchangeBuilder.directExchange(ITEM_EXCHANGE).durable(true).build(); } //声明队列 @Bean("itemQueue") public Queue itemQueue(){ return QueueBuilder.durable(ITEM_QUEUE).build(); } //绑定队列和交换机 @Bean public Binding itemQueueExchange(@Qualifier("itemQueue") Queue queue, @Qualifier("itemExchange") Exchange exchange){ return BindingBuilder.bind(queue).to(exchange).with("info").noargs(); } }
-
在生产者工程springboot-rabbitmq-producer中创建测试类,发送消息;
@SpringBootTest public class RabbitMQTest { @Autowired private RabbitTemplate rabbitTemplate; @Test public void test(){ // true:RabbitMQ会调用Basic.Return命令将消息返回给生产者 // false:RabbitMQ会把消息直接丢弃 rabbitTemplate.setMandatory(true); //设置ReturnsCallback rabbitTemplate.setReturnsCallback( returnedMessage -> { System.out.println("消息从交换机到队列失败"); }); //设置ConfirmCallback rabbitTemplate.setConfirmCallback(new RabbitTemplate.ConfirmCallback() { /** * @param correlationData 相关配置信息 * @param ack 交换机是否成功收到消息 * @param cause 错误信息 */ @Override public void confirm(CorrelationData correlationData, boolean ack, String cause) { if (ack){ System.out.println("成功发送消息到交换机"); }else { System.out.println("消息发送到交换机失败"); } } }); rabbitTemplate.convertAndSend(RabbitMQConfig.ITEM_EXCHANGE,"info", "rabbitmq send message ..."); } }
四、消费者代码
-
编写消息监听器com.example.rabbitmq_consumer.listener;
@Component public class MyListener { @RabbitListener(queues = "item_queue") public void printMsg(String msg, Channel channel, Message message) throws IOException { try{ //如果消息处理失败 //int num = 1/0; System.out.println("消息处理成功:" + msg); // deliveryTag:该消息的index // multiple:是否批量.true:将一次性ack所有小于deliveryTag的消息 // 这里表示该消息已经被消费了 可以在队列删掉 这样以后就不会再发了 channel.basicAck(message.getMessageProperties().getDeliveryTag(),false); }catch (Exception e) { System.out.println("消息处理失败:" + msg); // deliveryTag:该消息的index // multiple:是否批量.true:将一次性拒绝所有小于deliveryTag的消息。 // requeue:被拒绝的是否重新入队列 // 这里表示该消息没有被成功消费,并且将该消息重新入队列 channel.basicNack(message.getMessageProperties().getDeliveryTag(), false,true); } } }