美文网首页消息中间件
用rebbitMq来实现你的延迟队列功能

用rebbitMq来实现你的延迟队列功能

作者: jsondream | 来源:发表于2016-07-12 10:49 被阅读1420次

延迟队列

在我们的上一篇文章使用delayedQueue实现你本地的延迟队列
中提到了延迟队列的作用.

但是我们知道,利用delayedQueue实现的是一个单机的,而且是内存中的延迟队列,他并没有一个集群的支持,并且需要在对泵机的时候,消息消费异常的时候做相应的逻辑处理。

那么这样做的话,我们需要的工作量还是很大的,有没有什么东西是让我们不做这一部分的工作也能实现延迟队列的功能?

当然有了。答案是:rabbitMq

利用rabbitMq来实现延迟队列的功能

那么如何利用rabbitMq来实现延迟队列的功能呢?

请先注意一点,RabbitMQ本身没有直接支持延迟队列功能,但是可以通过以下特性模拟出延迟队列的功能。那么这是通过哪些特性呢,那就让我们来认识一下这两个特性吧.

  • Per-Queue Message TTL

    RabbitMQ可以对消息和队列设置TTL(过期时间)。

    RabbitMQ针对队列中的消息过期时间(Time To Live, TTL)有两种方法可以设置。第一种方法是通过队列属性设置,队列中所有消息都有相同的过期时间。第二种方法是对消息进行单独设置,每条消息TTL可以不同。如果上述两种方法同时使用,则消息的过期时间以两者之间TTL较小的那个数值为准。消息在队列的生存时间一旦超过设置的TTL值,就成为dead message,消费者将无法再收到该消息。

  • Dead Letter Exchanges

    利用DLX,当消息在一个队列中变成死信后,它能被重新publish到另一个Exchange,这个Exchange就是DLX。消息变成死信一向有以下几种情况:

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

    DLX也是一下正常的Exchange同一般的Exchange没有区别,它能在任何的队列上被指定,实际上就是设置某个队列的属性,当这个队列中有死信时,RabbitMQ就会自动的将这个消息重新发布到设置的Exchange中去,进而被路由到另一个队列,publish可以监听这个队列中消息做相应的处理,这个特性可以弥补RabbitMQ 3.0.0以前支持的immediate参数中的向publish确认的功能。

结合以上两个特性,就可以模拟出延迟消息的功能.

基于x-dead-letter-routing-key的单条消息延迟队列的java代码实现

生产者(发送)端代码:


import java.util.HashMap;
import java.util.concurrent.TimeoutException;

import com.rabbitmq.client.AMQP;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;

public class Send {
    //队列名称  
    private final static String QUEUE_NAME = "hello";  
  
    public static void main(String[] argv) throws Exception  
    {  
        
        /** 
         * 创建连接连接到MabbitMQ 
         */  
        ConnectionFactory factory = new ConnectionFactory();  
        //设置MabbitMQ所在主机ip或者主机名  
        factory.setHost("localhost");  
        //创建一个连接  
        Connection connection = factory.newConnection();
        //创建一个频道  
        Channel channel = connection.createChannel();  
        
        
 
        
        //指定一个队列  
        channel.queueDeclare(QUEUE_NAME, true, false, false, null);  
        //发送的消息  
        String message = "hello world!"+System.currentTimeMillis();;  

        AMQP.BasicProperties.Builder builder = new AMQP.BasicProperties.Builder();  
        AMQP.BasicProperties properties = builder.expiration("2000").deliveryMode(2).build();  
        //往队列中发出一条消息     这时候要发送的队列不应该是QUEUE_NAME,这样才能进行转发的
        channel.basicPublish("", "DELAY_QUEUE", properties, message.getBytes());  
        System.out.println(" [x] Sent '" + message + "'" );  
        //关闭频道和连接  
        channel.close();  
        connection.close();  
     }  
}

消费者(接受)端代码:


import java.util.HashMap;

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.QueueingConsumer;

public class Recv {
    // 队列名称
    private final static String QUEUE_NAME = "hello";

    public static void main(String[] argv) throws Exception {

        ConnectionFactory factory = new ConnectionFactory();
        factory.setHost("localhost");
        Connection connection = factory.newConnection();
        Channel channel = connection.createChannel();
        
        channel.queueDeclare(QUEUE_NAME, true, false, false, null); 
        // channel.queueBind(QUEUE_NAME, "amq.direct", QUEUE_NAME);

        HashMap<String, Object> arguments = new HashMap<String, Object>();  
        arguments.put("x-dead-letter-exchange", "amq.direct");  
        arguments.put("x-dead-letter-routing-key", QUEUE_NAME);  
        channel.queueDeclare("DELAY_QUEUE", true, false, false, arguments); 
        
        //声明队列,主要为了防止消息接收者先运行此程序,队列还不存在时创建队列。  
        //channel.queueDeclare(QUEUE_NAME, false, false, false, null);  
        System.out.println(" [*] Waiting for messages. To exit press CTRL+C"); 
      //创建队列消费者  
        QueueingConsumer consumer = new QueueingConsumer(channel);  
        //指定消费队列  
        channel.basicConsume(QUEUE_NAME, true, consumer);  
        while (true)  
        {  
            //nextDelivery是一个阻塞方法(内部实现其实是阻塞队列的take方法)  
            QueueingConsumer.Delivery delivery = consumer.nextDelivery();  
            String message = new String(delivery.getBody());  
            System.out.println(" [x] Received '" + message + "'"+ "'   [当前系统时间戳]" +System.currentTimeMillis());  
        }  
    }
}

参考资料

http://www.rabbitmq.com/ttl.html
http://www.rabbitmq.com/dlx.html
https://www.cloudamqp.com/docs/delayed-messages.html
http://www.netfoucs.com/article/xtjsxtj/73636.html#

相关文章

  • 用rebbitMq来实现你的延迟队列功能

    延迟队列 在我们的上一篇文章使用delayedQueue实现你本地的延迟队列中提到了延迟队列的作用. 但是我们知道...

  • RabbitMQ实现延迟消费

    RabbitMQ实现延迟消费 rabbitMQ没有能直接设置消息延迟消费的功能; 但是可以用消息超时,加入死信队列...

  • 通过redis的有序集合[zset] 实现延迟队列

    php使用redis的有序集合zset实现延迟队列 我们通过redis的有序集合zset来实现简单的延迟队列,将消...

  • 基于Rabbitmq实现延迟队列

    转自 基于Rabbitmq实现延迟队列 基于Rabbitmq实现延迟队列 延迟队列的使用场景 淘宝订单业务:下单后...

  • redis实现延时队列

    在工作中想实现一个延迟功能,一般会借助rocketmq或者kafka的延迟队列功能来实现,但是这俩个消息中间件都有...

  • redis队列的使用

    背景:项目需求里,想做一个可拓展的队列来存储名单。实现功能:该队列实现增删改的功能 我用的方法:采用了redis的...

  • 数组实现环形队列

    利用数组结构在队列的基础下用取模算法来实现环形队列。 环形队列拥有复用功能。 主要算法思想:1)front指向队列...

  • spring boot 集成rabbitmq 实现延迟队列

    rabbitmq 实现延迟队列 什么是延迟队列 延迟队列存储的对象肯定是对应的延时消息,所谓”延时消息”是指当消息...

  • Laravel源码分析 - Redis延迟队列(一)

    延迟队列,首先它是一个队列。然后其任务可以延迟被执行。实现队列,以及延迟队列的方式有很多种,基于Redis的方式也...

  • 队列之-队列实现栈

    一、队列实现栈核心算法概述 之前已经描述过了用栈实现队列的功能,见栈系列之-实现队列,那么同样队列也可以用来实现栈...

网友评论

    本文标题:用rebbitMq来实现你的延迟队列功能

    本文链接:https://www.haomeiwen.com/subject/lxojjttx.html