RabbitMQ 过期时间(TTL)(四)

1,666 阅读3分钟

这是我参与更文挑战的第 22 天,活动详情查看: 更文挑战

日积月累,水滴石穿 😄

前言

TTL(Time To Live),即过期时间。在RabbitMQ中分别可以对 消息和队列设置 TTL。

对消息设置TTL

RabbitMQ中有两种方法可以设置消息的 TTL :

  • 通过队列属性设置,队列中所有消息都有相同的过期时间。

  • 对消息本身进行单独设置,每条消息的 TTL 可以不同。 注:如果两种方法一起使用,则消息的 TTL 以两者之间较小的那个数值为准。消息在队列中的生存时间一旦超过设置 TTL 值时,就会变成“死信”( Dead Message ),消费者将无法再收到该消息

    注:如果队列设置了死信队列,那么这条消息就会被转发到死信队列上,该消息就可以被正常消费。关于死信队列我们之后再讲。

通过队列属性设置

channel.queueDeclare 方法中加入x-message-ttl参数,这个参数的单位是毫秒。 原生api

// 获取连接对象
Connection conn = RabbitMQUtil.createConn();
// 获取连接通道
Channel channel = conn.createChannel();
Map<String,Object> map = new HashMap<>();
map.put("x-message-ttl",10000);
channel.queueDeclare("ttl-quequ",true,false,true,map);

SpringBoot

  @Bean
    public Queue payQueue(){
        Map<String,Object> params = new HashMap<>();
        //设置队列的过期时间
        params.put("x-message-ttl",10000);
        //也可以使用下面这种写法
        //QueueBuilder.durable("ttl-quequ").ttl(10000).build();
        return QueueBuilder.durable("ttl-quequ").withArguments(params).build();
    }

image.png

如果不设置 TTL ,则表示此消息不会过期,如果将 TTL 设置为0,表示除非此时可以直接将消息投递到消费者,否则该消息会被立即丢弃。

对消息本身设置

channel.basicPublish 方法中加入 expiration属性参数,单位为毫秒。

原生api

// 获取连接对象
Connection conn = RabbitMQUtil.createConn();
// 获取连接通道
Channel channel = conn.createChannel();
//给消息设置ttl
channel.queueDeclare("ttl-quequ2",true,false,true,null);

channel.basicPublish("","ttl-quequ2",
                     new AMQP.BasicProperties().builder()
                     .expiration("30000")
                     .build(),
                     "设置消息过期时间".getBytes());

SpringBoot

 MessagePostProcessor messagePostProcessor = message -> {
        MessageProperties messageProperties = message.getMessageProperties();
            //设置信息过期时间
            messageProperties.setExpiration("30000");
            return message;
        };
        rabbitTemplate.convertAndSend("exchangeName","routingKey","消息内容",messagePostProcessor);

image.png image.png 对于第一种设置队列 TTL 属性的方法,一旦消息过期,就会从队列中抹去,而在第二种方法中,即使消息过期,也不会马上从队列中抹去,因为每条消息是否过期是在即将投递到消费者之前判定的。

为什么这两种方法处理的方式不一样?因为第一种方法,队列中己过期的消息肯定在队列头部(先进先出),RabbitMQ 只要定期从队列头开始扫描是否有过期的消息即可。而第二种方法里,每条消息的过期时间可以不同(第一条信息设置10s,第二条消息设置2s),如果要删除所有过期消息势必要扫描整个队列,所以不如等到此消息即将被消费时再判定是否过期,如果过期再进行删除即可。

对队列设置TTL

通过 channel.queueDeclare 方法中的 x-expires 参数可以控制队列被自动删除前处于未使用状态的时间。未使用的意思是队列上没有任何的消费者,队列也没有被重新声明。 即使有消费者连接上了,如果消费者将消息全部消费完,队列的过期时间也会重新计算。

RabbitMQ会确保在过期时间到达后将队列删除,但是不保障删除的动作有多及时 。在 RabbitMQ 重启后,持久化的队列的过期时间会被重新计算。

用于表示过期时间的 x-expires 参数以毫秒为单位 ,并且服从和 x-message-ttl一样的约束条件,不过不能设置为 0。比如该参数设置为 1000 ,则表示该队列如果在1 秒钟之内未被使用则会被删除。

原生api

public static void main(String[] args) throws IOException {
    // 获取连接对象
    Connection conn = RabbitMQUtil.createConn();
    // 获取连接通道
    Channel channel = conn.createChannel();

    Map<String, Object> map = new HashMap<>();
    map.put ("x-expires" , 30000);
    //给队列设置ttl
    //创建一个过期时间为 30 秒的队列
    channel.queueDeclare("ttl-quequ3",true,false,true,map);
}

SpringBoot

@Bean
public Queue payQueue(){
    Map<String,Object> params = new HashMap<>();
    //设置队列的过期时间
    params.put("x-expires",10000);
    //也可以使用下面这种写法
    //return QueueBuilder.durable(queueName).expires(5000).build();
    return QueueBuilder.durable(queueName).withArguments(params).build();
}

image.png

  • 如你对本文有疑问或本文有错误之处,欢迎评论留言指出。如觉得本文对你有所帮助,欢迎点赞和关注。

参考文献

朱忠华老师《RabbitMQ实战指南》一书。