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

延迟队列

在我们的上一篇文章使用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#

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

  • 顾名思义,延迟队列就是进入该队列的消息会被延迟消费的队列。而一般的队列,消息一旦入队了之后就会被消费者马上消费。 ...
    Java架构阅读 2,295评论 3 24
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 137,081评论 19 139
  • 背景 何为延迟队列? 顾名思义,延迟队列就是进入该队列的消息会被延迟消费的队列。而一般的队列,消息一旦入队了之后就...
    wooyoo阅读 3,556评论 0 17
  • 来源 RabbitMQ是用Erlang实现的一个高并发高可靠AMQP消息队列服务器。支持消息的持久化、事务、拥塞控...
    jiangmo阅读 10,560评论 2 34
  • 1.什么是消息队列 消息队列允许应用间通过消息的发送与接收的方式进行通信,当消息接收方服务忙或不可用时,其提供了一...
    zhuke阅读 4,643评论 0 12

友情链接更多精彩内容