中间件系列之RabbitMQ-5-过期时间与死信队列

1,441 阅读3分钟

存活时间

存活时间TTL(Time-To-Live)表示可以对消息设置预期的时间,在这个时间内都可以被消费者接受获取,过了这个时间后消息将被自动删除。RabbitMQ可以对消息和队列设置TTL。

通过队列属性设置TTL,队列中所有消息都有相同的存活时间,而对消息则可以进行单独设置。如果同时设置,则消息的存活时间以较小的为准。消息在队列的生存时间一旦超过设置的TTL值,就称为dead message, 消费者将无法再收到该消息。

设置队列属性

在queue.declare方法中加入x-message-ttl参数,单位为ms

Map<String, Object>  argss = new HashMap<String, Object>();
argss.put("x-message-ttl",6000);
channel.queueDeclare(queueName, durable, exclusive, autoDelete, argss);

设置消息属性

针对每条消息设置TTL的方法是在basic.publish方法中加入expiration的属性参数,单位为ms.

AMQP.BasicProperties.Builder builder = new AMQP.BasicProperties.Builder();
builder.expiration("6000");
AMQP.BasicProperties  properties = builder.build();        channel.basicPublish(exchangeName,routingKey,mandatory,properties,"ttlTestMessage".getBytes());

死信队列

DLX, Dead-Letter-Exchange。利用DLX, 当消息在一个队列中变成死信(dead message)之后,它能被重新publish到另一个Exchange,这个Exchange就是DLX。

DLX也是一个正常的Exchange,和一般的Exchange没有区别,它能在任何的队列上被指定,实际上就是设置某个队列的属性,当这个队列中有死信时,RabbitMQ就会自动的将这个消息重新发布到设置的Exchange上去,进而被路由到另一个队列。

消息变成死信有以下几种情况:

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

通过在queueDeclare方法中加入“x-dead-letter-exchange”实现。

channel.exchangeDeclare("some.exchange.name", "direct");
Map<String, Object> args = new HashMap<String, Object>();
args.put("x-dead-letter-exchange", "some.exchange.name");
channel.queueDeclare("myqueue", false, false, false, args);

你也可以为这个DLX指定routing key,如果没有特殊指定,则使用原队列的routing key

args.put("x-dead-letter-routing-key", "some-routing-key");

在实际生产环境中,我们还需要对死信队列进行一个监听和处理,当然具体的处理逻辑和业务相关。

延时队列

简单来说,延时队列就是用来存放需要在指定时间被处理的元素的队列。

利用我们上文提到的TTL+死信队列就能实现延时队列的功能.

生产者生产一条延时消息,根据需要延时时间的不同,利用不同的routingkey将消息路由到不同的延时队列,每个队列都设置了不同的TTL属性,并绑定在同一个死信交换机中,消息过期后,根据routingkey的不同,又会被路由到不同的死信队列中,消费者只需要监听对应的死信队列进行处理即可。

public class TimeoutDLX {
    public static void main(String[] args) throws IOException, TimeoutException {
        Connection connection = ConnectionUtil.getConnection();
        Channel channel = connection.createChannel();
        //创建DLX及死信队列
        channel.exchangeDeclare("dlx.exchange", "direct");
        channel.queueDeclare("dlx.queue", true, false, false, null);
        channel.queueBind("dlx.queue", "dlx.exchange", "dlx.routingKey");
        //创建测试超时的Exchange及Queue
        channel.exchangeDeclare("delay.exchange", "direct");
        Map<String, Object> arguments = new HashMap<>();
        //过期时间10s
        arguments.put("x-message-ttl", 10000);
        //绑定DLX
        arguments.put("x-dead-letter-exchange", "dlx.exchange");
        //绑定发送到DLX的RoutingKey
        arguments.put("x-dead-letter-routing-key", "dlx.routingKey");
        channel.queueDeclare("delay.queue", true, false, false, arguments);
        channel.queueBind("delay.queue", "delay.exchange", "delay.routingKey");
        //发布一条消息
        channel.basicPublish("delay.exchange", "delay.routingKey", null, "该消息将在10s后发送到延迟队列".getBytes());
        channel.close();
        connection.close();
    }
}

通过控制台可以看到队列信息变化:

延时10s后: