栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

rabbitmq+springboot延迟队列实现及发布确认

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

rabbitmq+springboot延迟队列实现及发布确认

延迟队列

使用插件实现延迟队列

因为使用死信队列去多延迟时,存在一个缺陷,就是指当第一个消息进入死信队列时,它的时间如果很大,后面进来的时间少的消息,也必须等待。

去官网下载对应版本的插件,如果版本不对的话,是安装不了的,这里用 rabbitmq_delayed_message_exchange.3.8.0.ez

使用插件后,延迟是由交换机去处理的,基本的处理结构:

生产者----->交换机延迟-------->队列-------->消费者

 安装插件后就会出现这种交换机类型

接着可以去准备交换机和队列

@Configuration
public class DelayedQueueConfig {
    //交换机
    public static final String DELAYED_EXCHANGE_NAME = "delayed.exchange";
    //队列
    public static final String DELAYED_QUEUE_NAME = "delayed.queue";
    //routingKey
    public static final String DELAYED_ROUTING_KEY = "delayed.routingKey";
    //队列
    @Bean
    public Queue delayedQueue(){
        return new Queue(DELAYED_QUEUE_NAME);
    }
    //交换机
    @Bean
    public CustomExchange delayedExchange(){
        Map arguments = new HashMap<>();
        arguments.put("x-delayed-type","direct");
        
        return new CustomExchange(DELAYED_EXCHANGE_NAME,"x-delayed-message",
                true,false,arguments);
    }
    //绑定
    @Bean
    public Binding delayedQueueBindingExchange(@Qualifier("delayedQueue") Queue delayedQueue,
                                               @Qualifier("delayedExchange") CustomExchange delayedExchange){


        return BindingBuilder.bind(delayedQueue).to(delayedExchange).with(DELAYED_ROUTING_KEY).noargs();
//自定义的交换机需要加.noargs()
    }
}

创建生产者

@Slf4j
@RestController
@RequestMapping("/ttl")
public class SendMsgController {
    @Autowired
    private RabbitTemplate rabbitTemplate;
    @GetMapping("/sendDelayedMsg/{message}/{delayTime}")
    public void sendMsg2(@PathVariable String message,@PathVariable Integer delayTime){
        log.info("当前时间:{},发送时长{}毫秒的信息给延迟队列delayed.queue:{}",
                new Date().toString(),delayTime,message);

        rabbitTemplate.convertAndSend(DelayedQueueConfig.DELAYED_EXCHANGE_NAME,
                DelayedQueueConfig.DELAYED_ROUTING_KEY,message, message1 -> {
            //发送延迟消息 延迟时长  ms
            message1.getMessageProperties().setDelay(delayTime);
            return message1;
        });
    }
}

创建消费者

@Slf4j
@Component
public class DelayQueueConsumer {
    //开始监控
    @RabbitListener(queues = DelayedQueueConfig.DELAYED_QUEUE_NAME)
    public void receiveDelayQueue(Message message){
        String msg = new String(message.getBody());
        log.info("当前时间:{},收到延迟队列消息:{}",new Date().toString(),msg);
    }
}

使用效果

都按照设定的情况去运行了没有出错

rabbitmq来做延迟队列可以很好的利用rabbitmq的特性,去做到其他事情,如,消息可靠发送,消息可靠投递,死信队列来保障消息至少被消费一次以及未被正确处理的消息不会被丢弃。另外,通过rabbitmq集群的特性,可以很好的解决单点故障问题,不会因为单个节点挂掉导致延迟队列不可用或消息丢失。

reids的zset 和java的DelayQueue 还有kafka的时间轮也可以选择

发布确认

当出现不明原因的问题的时候,处理无法投递的消息

配置文件中增加属性:

publisher-confirm-type: correlated 发布确认模式

交换机和队列继续使用上面的

生产者(我写的时候用了新的交换机和队列,但是是不必要的)

@Slf4j
@RestController
@RequestMapping("/confirm")
public class ProducerController {

    @Autowired
    private RabbitTemplate rabbitTemplate;

    @GetMapping("/sendMsg/{message}")
    public void sendMessage(@PathVariable String message){
        CorrelationData correlationData = new CorrelationData("1");
        rabbitTemplate.convertAndSend(ConfirmConfig.CONFIRM_EXCHANGE_NAME,
                ConfirmConfig.CONFIRM_ROUTING_KEY,message,correlationData);
        log.info("发送消息:{}",message);

    }

}

消费者

@Slf4j
@Component
public class Consumer {
    @RabbitListener(queues = ConfirmConfig.CONFIRM_QUEUE_NAME)
    public void receiveConfirmMessage(Message message){
        String s = new String(message.getBody());
        log.info("接受到的队列{}消息:{}",ConfirmConfig.CONFIRM_QUEUE_NAME,s);
    }
}

回调接口

@Slf4j
@Component
public class MyCallBack implements RabbitTemplate.ConfirmCallback,RabbitTemplate.ReturnsCallback {
    //这个注解比下面的优先级高
    @Autowired
    private RabbitTemplate rabbitTemplate;
    //注入到接口中
    @PostConstruct
    public void init(){
        rabbitTemplate.setConfirmCallback(this);
        rabbitTemplate.setReturnsCallback(this);
    }
    
    @Override
    public void confirm(CorrelationData correlationData, boolean ack, String cause) {

        String id = correlationData != null ? correlationData.getId() : "";
        if(ack){
            log.info("交换机以及收到id为:{}的消息",id);
        }else{
            log.info("交换机还未收到id为:{}的消息,原因:{}",id,cause);

        }
    }
    //可以在消息传递过程中不可以达目的地时 将消息返回给生产者
    //只有不可达目的地的时候 才回退
    @Override
    public void returnedMessage(ReturnedMessage returned) {

        log.error("消息{},被交换机{}退回,退回原因:{},路由key:{}",
                returned.getMessage(),returned.getExchange(),returned.getRoutingKey());
    }

}

有两种出现错误的情况

一种是发到交换机的时候就出现错误

要在配置文件中再加属性:

publisher-returns: true

比如在生产者中,把交换机的名字写错,就会失败

回调接口中的confirm方法就是处理这个的,处理后

 第二种是,交换机以及收到,但是发送给队列时出错

方法returnedMessage就是用于回调的

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/1024250.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号