RabbitMQ实现可靠性传输 代码篇

本文为代码篇,建议先看理论篇,点击跳转


实现可靠性传输的理论篇对应的实操代码。

业务相关的都用了伪代码,方便理解。


环境:

springboot 2.1.9.RELEASE + amqp-client-5.4.3.jar

生产者确认模式

application.properties

spring.rabbitmq.publisher-confirms=true

主动分配唯一Id:(这里其实需要预处理器MessagePostProcessor,在持久化的内容中有讲)

public void sendMessage(String msg) {
        CorrelationData correlationData = new CorrelationData();
		correlationData.setId(唯一Id);
    	redis设置Id 与 msg 一一对应
        rabbitTemplate.convertAndSend(交换机, 路由键, msg, correlationData);
    }

confirmCallback接口,监听Broker发送的通知

 rabbitTemplate.setConfirmCallback(new RabbitTemplate.ConfirmCallback() {
            
            @Override
            public void confirm(CorrelationData correlationData, boolean ack, String cause) {
                // correlationData 与 主动分配的唯一Id有关
                String uniqueId = correlationData.getId();
                if(ack){
                    根据uniqueId,删除redis中的记录
                }else {
                     // ack = false ,相当于回复nack
                    不处理,由定时任务进行重发
                }
            }
        });

定时任务:

@EnableScheduling
@Component
public class FixedTimeTask {
    @Scheduled(cron = "*/30 * * * * ?")
    public void execute() {
        扫描redis中,所有未发送的键。
        if(重发未超次数限制){
            重发
        }else{
            入库,记录消息,人工处理。
            注意删除redis中的相关键
        }
    }
}

无法被路由的消息处理
  1. 失败确认

    application.properties

    spring.rabbitmq.publisher-returns=true
    
    // 两者都必须设置,才能触发returnCallBack
            rabbitTemplate.setMandatory(true);
            
            rabbitTemplate.setReturnCallback(new RabbitTemplate.ReturnCallback() {
                @Override
                public void returnedMessage(Message message, int replyCode, 
                                            String replyText, String exchange, String routingKey) {
                    相关处理代码...
                }
            });
    
    
  2. 声明交换机时,指定备份的交换机 , eg:

     @Bean
        public DirectExchange secKillExchange() {
             Map<String, Object> argsMap = new HashMap<String, Object>();
            argsMap.put("alternate-exchange", MIAOSHA_EXCHANGE_BAK);
            DirectExchange directExchange = new DirectExchange(MIAOSHA_EXCHANGE,true,false,argsMap);
            return directExchange;
        }
    

拓:若方案1、2一起使用,方案2生效,方案1失效


如何持久化消息队列

设置持久化,两个步骤:必须都设置

  1. 创建 exchange、queue 时,设置为持久化 durable = true

    (不演示,默认就是true)

  2. 发送消息时,将消息Properties的 deliveryMode=PERSISTENT

    MessagePostProcessor的原理可见另一篇博文,点击跳转

rabbitTemplate.setBeforePublishPostProcessors(correlationIdProcessor());

@Bean
    /***
     * @Description: 绑定correlationId、以及消息持久化
     **/
    public MessagePostProcessor correlationIdProcessor() {
        MessagePostProcessor messagePostProcessor = new MessagePostProcessor() {
            @Override
            public Message postProcessMessage(Message message, Correlation correlation) {
                MessageProperties messageProperties = message.getMessageProperties();

                if (correlation instanceof CorrelationData) {
                    String correlationId = ((CorrelationData) correlation).getId();
                    messageProperties.setCorrelationId(correlationId);
                }
                // 持久化处理
                messageProperties.setDeliveryMode(MessageDeliveryMode.PERSISTENT);
                return message;
            }

            @Override
            public Message postProcessMessage(Message message) throws AmqpException {
                MessageProperties messageProperties = message.getMessageProperties();
                messageProperties.setDeliveryMode(MessageDeliveryMode.PERSISTENT);
                return message;
            }
        };
        return messagePostProcessor;
    }

如何保证消费成功(或限流处理)

application.properties

# manual ,手动确认
spring.rabbitmq.listener.direct.acknowledge-mode=manual

# prefetch,预抓取。 等价于缓冲区大小 。 限流关键
spring.rabbitmq.listener.direct.prefetch=100
@RabbitListener(queues = 队列名)
@RabbitHandler
    public void receiveDeadMsg(String msg, Channel channel, Message messages) throws Exception {
        try {
            业务代码...
                // 回发ack,第二个参数multiple,表示是否批量确认,具体请百度,不赘述
            channel.basicAck(messages.getMessageProperties().getDeliveryTag(), false);
        } catch (Exception e) {
            // 回发Nack,且重回队列为false,防死循环
            channel.basicNack(messages.getMessageProperties().getDeliveryTag(), false, false);
        }
    }

此处有需要,可以阅读另一篇博文:RabbitMQ 消费者确认auto 和 manual 模式对异常的处理区别(含重试、requeue的影响)


本文完,有误欢迎指出。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值