rabbitmq中的延迟队列与ttl具体实践

业务场景:订单系统中的订单在规定时间内未支付,就需要修改订单的状态,在开会系统中的,在提前时间内需要对参会的人员发送消息通知等等,需要在一个时间之前或者之后时间点进行操作的数据
原理就是将rabbitmq中的ttl特性与死信队列结合起来实现延迟队列的
TTL:对队列的生存时间进行设置 对一条消息进行TTL设置好 队列的ttl会在规定时间内,消息死亡 消息中的ttl则会受到队列中的前面消息影响
下面代码主要是对队列的ttl进行实践:下面的代码就是对队列配置进行定义的

@SpringBootConfiguration
public class TtlDelayConfig {

    public static final String TTL_DELAY_EXCHANGE = "ttlDelayExchange";

    public static final String DEAD_LETTER_EXCHANGE = "ttlDeadExchange";

    public static final String TTL_ROUTY_KEY = "ttlRoutyKeyA";

    public static final String TTL_ROUTY_KEY_B = "ttlRoutyKeyB";

    public static final String TTL_DELAY_QUEUE = "ttlDelayQueu";

    public static final String TTL_DELAY_QUEUEB = "ttlDelayQue2";

    public static final String DEAD_LETTER_QUEUE = "deadLetterQueue";

    public static final String DEAD_LETTER_QUEUEB = "deadLetterQueueB";

    public static final String ROUTY_KEY_QUEUE_EXCHANGE = "routyKeyA";

    public static final String ROUTY_KEY_QUEUE_EXCHANGEB = "routyKeyB";


    /**
     * 因为在同一个模块中命名了多个队列以及交换机,存在很多bean的名称相同的情况
     * 所以对于bean的名称不是很规范
     * 流程就是往延迟对列中发送消息,同时对队列中死信路由属性,以及交换机,ttl属性设置好对应的时间
     * 只要队列中的消息在规定时间内没有被消费掉,那么就会变成死信,只需监听死信队列中对队列中的消息进行消费
     * 
     */


    /**
     * 声明一个延迟队列
     * @return
     */
    @Bean("deadLetterQueueB05")
    public DirectExchange directExchange(){
        return new DirectExchange(TTL_DELAY_EXCHANGE);
    }


    /**
     * 声明一个死信交换机
     * @return
     */
    @Bean("deadLetterQueueB03")
    public DirectExchange deadLetterExchange(){
        return new DirectExchange(DEAD_LETTER_EXCHANGE);
    }

    /**
     * 定义好一个超时的队列
     * @return
     */
    @Bean("deadLetterQueueB01")
    public Queue delayQueueA(){
        Map<String, Object> map = new HashMap<>();
        //设置好死信的交换机
        map.put("x-dead-letter-exchange", DEAD_LETTER_EXCHANGE);
        //设置好死信与队列的路由键
        map.put("x-dead-letter-routing-key", TTL_ROUTY_KEY);
        //设置队列的ttl时间-设置好10秒
        map.put("x-message-ttl", 1000);
        return QueueBuilder.durable(TTL_DELAY_QUEUE).withArguments(map).build();
    }


    /**
     * 定义好一个超时时间为6秒的队列
     * @return
     */
    @Bean("deadLetterQueue09")
    public Queue delayQueueB(){
        Map<String, Object> map = new HashMap<>();
        //设置好死信的交换机
        map.put("x-dead-letter-exchange", DEAD_LETTER_EXCHANGE);
        //设置好死信与队列的路由键
        map.put("x-dead-letter-routing-key", TTL_ROUTY_KEY_B);
        //设置队列的ttl时间-设置好10秒
        map.put("x-message-ttl", 6000);
        return QueueBuilder.durable(TTL_DELAY_QUEUEB).withArguments(map).build();
    }


    /**
     * 声明一个队列用来处理超时10秒的死信
     * @return
     */
    @Bean("dQueue09")
    public Queue deadLetterQueueA(){
        return new Queue(DEAD_LETTER_QUEUE);
    }


    /**
     * 声明一个队列用来处理超时6秒的死信
     * @return
     */
    @Bean("eue09")
    public Queue deadLetterQueueB(){
        return new Queue(DEAD_LETTER_QUEUEB);
    }


    /**
     * 声明延迟队列与交换机的绑定
     * @return
     */
    @Bean("deadL09")
    public Binding delayBindA(){
        return BindingBuilder.bind(delayQueueA()).to(directExchange()).with(ROUTY_KEY_QUEUE_EXCHANGE);
    }


    /**
     * 声明延迟队列与交换机的绑定
     * @return
     */
    @Bean("de9")
    public Binding delayBindB(){
        return BindingBuilder.bind(delayQueueB()).to(directExchange()).with(ROUTY_KEY_QUEUE_EXCHANGEB);
    }


    /**
     * 声明死信队列与交换机的绑定
     * @return
     */
    @Bean
    public Binding deadBindA(){
        return BindingBuilder.bind(deadLetterQueueA()).to(deadLetterExchange()).with(TTL_ROUTY_KEY);
    }


    /**
     * 声明死信队列与交换机的绑定
     * @return
     */
    @Bean
    public Binding deadBindB(){
        return BindingBuilder.bind(deadLetterQueueB()).to(deadLetterExchange()).with(TTL_ROUTY_KEY_B);
    }
}

下面的代码需要编写一个发送消息的方法来发送消息:

@Component
public class SendMsg {

    @Autowired
    private RabbitTemplate rabbitTemplate;

    public void sendMsg(Object obj, Integer type){
        switch (type){
            case 10:
                rabbitTemplate.convertAndSend(TTL_DELAY_EXCHANGE, ROUTY_KEY_QUEUE_EXCHANGE, obj );
                break;
            case 6:
                rabbitTemplate.convertAndSend(TTL_DELAY_EXCHANGE, ROUTY_KEY_QUEUE_EXCHANGEB, obj);
                break;

        }
    }

在controller编写代码进行访问:


    @Autowired
    private SendMsg sendMsg;

    @RequestMapping(value = "sendDeadMessge", method = RequestMethod.GET)
    public String sendDeadMessage(){
        rabbitTemplate.convertAndSend("businessExcahnge", "", "deadletter");
        return "dead";
    }


    @RequestMapping(value = "ttl", method = RequestMethod.GET)
    public String send(String msg, Integer type){
        log.info("当前时间为:{}," , LocalDateTime.now());
        sendMsg.sendMsg("hello ttl", type);
        return "ttl";
    }

下面就是利用消息中的ttl属性来对实现延迟队列的代码
延迟队列中的配置类代码如下:

@SpringBootConfiguration
public class DelayConfig {

    //设置延迟的交换机
    public static final String DELAY_EXCAHNGE = "delay_exchange";

    //设置死信交换机
    public static final String DEAD_EXCHANGE = "dead_exchange";

    //设置好延迟队列
    public static final String DELAY_QUEUE = "delay_business_queue";

    //设置好死信队列
    public static final String DEAD_QUEUE = "deadLetterQueueC";

    //设置好延迟队列的路由键
    public static final String DELAY_ROUTY_KEY = "delayMessage";

    //设置好死信队列的路由键
    public static final String DEAD_ROUTY_KEY = "deadMessage";

    /**
     * 由于是在一个项目配置了多个queue导致bean的名称存在相同
     * 所以队列或者交换机的bean的命名不是很规范
     * 流程:与队列中的流程有很大的区别:
     * 它是通过对消息中的ttl属性进行了时间的限制,如果消息没被消费就会丢弃到
     * 队列设置好的死信交换机中去,最大的缺点就是:会受当前消息消费情况的影响,不消费,后面设置的消息也就永远不会
     * 被消费掉,可以用代码测试得出结论
     */


    /**
     * 声明一个延迟交换机
     * @return
     */
    @Bean("delayExchange1")
    public DirectExchange directExchange(){
        return new DirectExchange(DELAY_EXCAHNGE);
    }


    /**
     * 声明死信交换机
     * @return
     */
    @Bean("dead_Exchange2")
    public DirectExchange directDeadExchange(){
        return new DirectExchange(DEAD_EXCHANGE);
    }


    /**
     * 声明一个延迟队列并且设置好死信属性
     * @return
     */
    @Bean("delayQueue1")
    public Queue delayQueue(){
        Map<String, Object> map = new HashMap<>();
        //添加死信交换机
        map.put("x-dead-letter-exchange", "dead_exchange");
        //声明死信交换机的路由键
        map.put("x-dead-letter-routing-key", "dead_letter_message");
        return QueueBuilder.durable(DELAY_QUEUE).withArguments(map).build();
    }


    /**
     * 声明一个死信队列
     * @return
     */
    @Bean("deadLetterQueueC1")
    public Queue deadLetterQueueC(){
        return new Queue(DEAD_QUEUE);
    }


    /**
     * 绑定一个延迟队列与交换机
     * @return
     */
    @Bean
    public Binding delayBinding(){
        return BindingBuilder.bind(delayQueue()).to(directExchange()).with(DELAY_ROUTY_KEY);
    }


    /**
     * 绑定一个死信队列与死信交换机
     * @return
     */
    @Bean
    public Binding deadLetterBinding(){
        return BindingBuilder.bind(deadLetterQueueC()).to(directDeadExchange()).with("dead_letter_message");
    }

下面需要编写一个消息发送类,需要对消息的时间属性进行设置:

/**
     * 给消息设置好ttl时间设置
     * @param obj
     * @param type
     */
    public  void delayMsg(String obj, Integer type){
        //给消息设定好超时限制
        MessageProperties messageProperties = new MessageProperties();
        messageProperties.setExpiration(type.toString()); // 设置过期时间,单位:毫秒
        byte[] msgBytes = obj.getBytes();
        Message message = new Message(msgBytes, messageProperties);
        rabbitTemplate.convertAndSend(DELAY_EXCAHNGE, DELAY_ROUTY_KEY, message);
    }

通过controller层来实现代码访问:

@RequestMapping(value = "feature", method = RequestMethod.GET)
    public String sendOneMessage(String msg, Integer type){
        log.info("当前时间为:{}," , LocalDateTime.now());
        sendMsg.delayMsg("hello everyone", type);
        return "one ttl";
    }

rabbitmq中的插件实现延迟队列,可以很好解决前面两点,不受队列中的前一条消息影响
具体上代码: 配置类:

//队列名称
    public static final String DELAYED_QUEUE_QUEUE_NAME = "delay.queue.demo.delay.queue";

    //延迟交换机的名称
    public static final String DELAYED_EXCHANGE_NAME = "delay.queue.demo.delay.exchange";

    //延迟队列与交换机的路由键
    public static final String DELAY_ROUTYING_KEY = "delay.queue.demo.delay.routykey";


    /**
     * 定义一个延迟队列的名称
     * @return
     */
    @Bean
    public Queue immediateQueue(){
        return new Queue(DELAYED_QUEUE_QUEUE_NAME);
    }


    /**
     * 交换机
     * @return
     */
    @Bean
    public CustomExchange customExchange(){
        Map<String, Object> args = new HashMap<>();
        args.put("x-delayed-type", "direct");
        return new CustomExchange(DELAYED_EXCHANGE_NAME, "x-delayed-message", true, false, args);
    }


    /**
     * 将队列与交换机通过路由键进行绑定
     * @return
     */
    @Bean
    public Binding bindingNotify(){
        return BindingBuilder.bind(immediateQueue()).to(customExchange()).with(DELAY_ROUTYING_KEY).noargs();
    }

发送消息的类:

/**
     * 给消息设置好ttl时间设置
     * @param obj
     * @param type
     */
    public  void delayMsg(String obj, Integer type){
        //给消息设定好超时限制
//        MessageProperties messageProperties = new MessageProperties();
//        messageProperties.setExpiration(type.toString()); // 设置过期时间,单位:毫秒
//        byte[] msgBytes = obj.getBytes();
//        Message message = new Message(msgBytes, messageProperties);
        rabbitTemplate.convertAndSend(DELAYED_EXCHANGE_NAME, DELAY_ROUTYING_KEY,  obj, message -> {
            // 注意这里时间可以使long,而且是设置header
            message.getMessageProperties().setHeader("x-delay",type);
            return message;
        });```
       
       消息监听类:
       @RabbitListener(queues = "delay.queue.demo.delay.queue")
    public void receiceB(Message message, Channel channel) throws IOException {
        String msg = new String(message.getBody());
        log.info("当前接收的信息插件86218376218831279数据为:{},时间为{}", msg, LocalDateTime.now());
        channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
    }
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值