RabbitMQ 保证消息可靠性

消息确认机制逻辑

1、消息抵达MQ服务器,会触发ConfirmCallback这个方法.
2、消息从交换机抵达对应的队列,成功不会触发,失败了会触发ReturnCallback这个方法.
3、消费者消费了消息之后,需要执行签收动作,也就是使用 basicAck() 手动签收,之后这个消息会从服务器删掉。

RabbitMQ 保证消息可靠性_第1张图片

修改配置

1、发送端确认回调 生产者到broker服务端 
# 消息抵达服务器 就会触发回调
spring.rabbitmq.publisher-confirms=true
2、发送端确认回调 exchange 到 queue 投递成功不会触发,失败才会触发
# 开启发送端消息抵达队列确认机制 exchange 到 queue
spring.rabbitmq.publisher-returns=true
# 如果消息抵达了队列 就会优先执行这个回调  以异步的模式
spring.rabbitmq.template.mandatory=true
3、消费端确认机制
# 开启手动确认
spring.rabbitmq.listener.simple.acknowledge-mode=manual

代码实现

@Slf4j
@Configuration
public class MyRabbitConfig {

    @Autowired
    RabbitTemplate rabbitTemplate;

    /**
     * 自定义序列化方式 将字节码换成json
     *
     * @return
     */
    @Bean
    public MessageConverter converter() {
        return new Jackson2JsonMessageConverter();
    }

    // MyRabbitConfig对象创建完成之后,执行这个方法
    // Constructor(构造方法) -> @Autowired(依赖注入) -> @PostConstruct(注释的方法)
    @PostConstruct
    public void initRabbit() {

        // 设置发送端确认回调 生产者到broker服务端
        rabbitTemplate.setConfirmCallback(new RabbitTemplate.ConfirmCallback() {
            /**
             * 设置发送端确认回调 生产者到broker服务端
             * 只要消息抵达服务器 就会触发回调  spring.rabbitmq.publisher-confirms=true
             * @param correlationData 当前消息的唯一ID
             * @param ack 消息是否成功或失败 true/false
             * @param cause 失败的原因
             */
            @Override
            public void confirm(CorrelationData correlationData, boolean ack, String cause) {
                log.info("消息抵达服务端:confirm:correlationData:{}", correlationData);
                log.info("消息抵达服务端:confirm:ack:{}", ack);
                log.info("消息抵达服务端:confirm:cause:{}", cause);

            }
        });

        // 设置发送端确认回调 exchange 到 queue 投递成功不会触发,失败才会触发
        rabbitTemplate.setReturnCallback(new RabbitTemplate.ReturnCallback() {
            /**
             * 消息抵达队列触发回调 exchange 到 queue ,只有消息没有到达队列才会触发这个回调,类似失败回调
             * # 开启发送端消息抵达队列确认机制 exchange 到 queue
             * spring.rabbitmq.publisher-returns=true
             * # 如果消息抵达了队列 就会优先执行这个回调  以异步的模式
             * spring.rabbitmq.template.mandatory=true
             * @param message 投递失败消息的详细内容
             * @param replyCode 回复的状态码
             * @param replyText 回复的文本内容
             * @param exchange 当时这个消息发送给哪个交换机
             * @param routingKey 当时这个消息指定的路由键
             */
            @Override
            public void returnedMessage(Message message, int replyCode, String replyText, String exchange, String routingKey) {
                log.info("失败的消息:returnedMessage:message:{}", message);
                log.info("失败的消息:returnedMessage:replyCode:{}", replyCode);
                log.info("失败的消息:returnedMessage:replyText:{}", replyText);
                log.info("失败的消息:returnedMessage:exchange:{}", exchange);
                log.info("失败的消息:returnedMessage:routingKey:{}", routingKey);
            }
        });
    }
}
         // 消费端确认机制 保证每一个消息被正确消费 此时才能broker删除这个消息
        // # 手动确认
        // spring.rabbitmq.listener.simple.acknowledge-mode=manual
        // 默认是自动确认的 消费端只要消息接收到,消费端会自动确认,服务端就会移除这个消息
        // 问题:收到很多消息,自动回复给服务器ACK 只有一个消息处理成功,服务宕机了,发生了消息丢失,原因是自动确认了
        // 手动确认:只要没有明确告诉MQ,消息没有被签收,没有ACK MQ就会一直存在 UACK状态,即使服务器宕机消息也不会丢失,会重新变为Ready状态,下次
        //         有新的消费者就会把消息发送给他,保证消费者消息不丢失。
        //         basicAck() 手动签收 业务成功完成
        //         basicNack() 拒签 业务失败 拒签


@RabbitHandler
public void processMessage2(String message,Channel channel,@Header(AmqpHeaders.DELIVERY_TAG) long tag) {
    System.out.println(message);
    try {
        channel.basicAck(tag,false);            // 确认消息
    } catch (IOException e) {
        e.printStackTrace();
    }
}

你可能感兴趣的:(消息中间件,JAVA,java-rabbitmq,rabbitmq,java)