zoukankan      html  css  js  c++  java
  • RabbitMQ如何实现延迟队列?(转)

    什么是延迟队列

    延迟队列存储的对象肯定是对应的延迟消息,所谓”延迟消息”是指当消息被发送以后,并不想让消费者立即拿到消息,而是等待指定时间后,消费者才拿到这个消息进行消费。

    场景一:在订单系统中,一个用户下单之后通常有30分钟的时间进行支付,如果30分钟之内没有支付成功,那么这个订单将进行一场处理。这是就可以使用延迟队列将订单信息发送到延迟队列。

    场景二:用户希望通过手机远程遥控家里的智能设备在指定的时间进行工作。这时候就可以将用户指令发送到延迟队列,当指令设定的时间到了再将指令推送到只能设备。


    RabbitMQ怎么实现延迟队列

    AMQP协议,以及RabbitMQ本身没有直接支持延迟队列的功能,但是可以通过TTL和DLX模拟出延迟队列的功能。

    TTL(Time To Live)

    RabbitMQ可以针对Queue和Message设置 x-message-tt,来控制消息的生存时间,如果超时,则消息变为dead letter

    RabbitMQ针对队列中的消息过期时间有两种方法可以设置。

    • A: 通过队列属性设置,队列中所有消息都有相同的过期时间。
    • B: 对消息进行单独设置,每条消息TTL可以不同。

    如果同时使用,则消息的过期时间以两者之间TTL较小的那个数值为准。消息在队列的生存时间一旦超过设置的TTL值,就成为dead letter

    详细可以参考:RabbitMQ之TTL(Time-To-Live 过期时间)

    DLX (Dead-Letter-Exchange)

    RabbitMQ的Queue可以配置x-dead-letter-exchange 和x-dead-letter-routing-key(可选)两个参数,如果队列内出现了dead letter,则按照这两个参数重新路由。

    • x-dead-letter-exchange:出现dead letter之后将dead letter重新发送到指定exchange
    • x-dead-letter-routing-key:指定routing-key发送

    队列出现dead letter的情况有:

    • 消息或者队列的TTL过期
    • 队列达到最大长度
    • 消息被消费端拒绝(basic.reject or basic.nack)并且requeue=false

    利用DLX,当消息在一个队列中变成死信后,它能被重新publish到另一个Exchange。这时候消息就可以重新被消费。

    详细可以参考:RabbitMQ之死信队列


    代码示例

    首先建立2个exchange和2个queue:

    • exchange_delay_begin:这个是producer端发送时调用的exchange, 将消息发送至queue_dealy_begin中。
    • queue_delay_begin: 通过routingKey=”delay”绑定exchang_delay_begin, 同时配置DLX=exchange_delay_done, 当消息变成死信时,发往exchange_delay_done中。
    • exchange_delay_done: 死信的exchange, 如果不配置x-dead-letter-routing-key则采用原有默认的routingKey,即queue_delay_begin绑定exchang_delay_beghin采用的“delay”。
    • queue_delay_done:消息在TTL到期之后,最终通过exchang_delay_done发送值此queue,消费端通过消费此queue的消息,即可以达到延迟的效果。

    1. 建立exchange和queue的代码(当然这里可以通过RabbitMQ的管理界面来实现,无需code相关代码):

    channel.exchangeDeclare("exchange_delay_begin", "direct", true);
    channel.exchangeDeclare("exchange_delay_done", "direct", true);
    
    Map<String, Object> args = new HashMap<String, Object>();
    args.put("x-dead-letter-exchange", "exchange_delay_done");
    channel.queueDeclare("queue_delay_begin", true, false, false, args);
    channel.queueDeclare("queue_delay_done", true, false, false, null);
    
    channel.queueBind("queue_delay_begin", "exchange_delay_begin", "delay");
    channel.queueBind("queue_delay_done", "exchange_delay_done", "delay");

    2. consumer端代码:

    QueueingConsumer consumer = new QueueingConsumer(channel);
    channel.basicConsume("queue_delay_done", false, consumer);
    
    while (true) {
        QueueingConsumer.Delivery delivery = consumer.nextDelivery();
        String msg = new String(delivery.getBody());
        System.out.println("receive msg time:" + new Date() + ", msg body:" + msg);
        channel.basicAck(delivery.getEnvelope().getDeliveryTag(), false);
    }

    3. producer端代码:设置消息的延迟时间为1min。

    AMQP.BasicProperties.Builder builder = new AMQP.BasicProperties.Builder();
    builder.expiration("60000");//设置消息TTL
    builder.deliveryMode(2);//设置消息持久化
    AMQP.BasicProperties properties = builder.build();
    
    String message = String.valueOf(new Date());
    channel.basicPublish("exchange_delay_begin","delay",properties,message.getBytes());

    在创建完exchange和queue之后,首先执行consumer端的代码,之后执行producer端的代码,待producer发送完毕之后,查看consumer端的输出:

    receive msg time:Tue Feb 14 21:06:19 CST 2017, msg body:Tue Feb 14 21:05:19 CST 2017

    可以看到延迟1min消费了相关消息。大功告成~

    欲了解更多消息中间件的内容,可以关注:消息中间件收录集


    参考资料

    1. rabbitmq 实现延迟队列的两种方式
    2. RabbitMQ之TTL(Time-To-Live 过期时间)
    3. RabbitMQ之死信队列

    转自:https://honeypps.com/mq/rabbitmq-how-to-make-delay-queue/

  • 相关阅读:
    入门篇:Ubuntu用apache做web服务器
    Linux上vi(vim)编辑器使用教程
    vim打开文档和多文档编辑
    vim常用命令
    进行有效编辑的七种习惯
    Ubuntu Nginx 开机自启动
    UBUNTU SERVER 12.04搭建PHP环境
    ubuntu下安装Apache+PHP+Mysql
    Ubuntu 12.04下LAMP安装配置
    data warehouse 1.0 vs 2.0
  • 原文地址:https://www.cnblogs.com/guanbin-529/p/12984827.html
Copyright © 2011-2022 走看看