说明
在之前的一篇博文《springboot学习(十三):RabbitMQ的使用 实现消息延迟消费》中,我使用RabbitMQ来达到失败消息的延迟重试的目的。其中就使用了消息的TTL和 死信交换机( Dead Letter Exchanges)。本篇博文我将继续翻译学习官方文档中对消息和队列的TTL的相关详解,通过本文我们将了解消息的TTL和队列的TTL,在设置时的差别等等内容。
正文
概述
RabbitMQ允许你为消息和队列设置有效期TTL(time to live),可以在队列声明时使用可选参数设置,也可以使用policies命令设置。官方推荐使用后者进行设置。
消息的TTL可以应用于一个队列,一组队列又或者针对每个消息进行设置。
基于队列的消息TTL
为一个队列设置消息的TTL,可以通过命令指定policy的message-ttl参数值,或者在队列声明时,指定相同名称的参数值。
当队列中的消息存留时间超过了配置的生存时间(TTL),则称该消息已死亡。注意,同一个消息被路由到不同的队列将拥有不同的过期时间,又或者永远不会过期。这取决于消息所存在的队列。一个队列中的死亡消息不会影响到其他队列中与之相同消息的生命周期。
服务器能保证在使用basic.deliver(推模式)或basic.get-ok(拉模式)的方式获取消息时,死亡消息将不会被传递给消费者。而且,服务器将尝试在消息过期时或之后将其删除。
不管是声明队列时设置的TTL的值还是通过policy设置的值,该值都只能是非负整数(n >=0),并且其时间单位为毫秒(ms)。所以当值为1000时表示消息被路由到队列后,可以存留1s或者直到它被传递给队列。该参数可以是AMQP 0-9-1的类型 short-short-int, short-int, long-int, long-long-int。
通过policy为队列设置消息的TTL
使用policy设置消息的TTL,需要执行“message-ttl”的值:
rabbitmqctl set_policy TTL ".*" '{"message-ttl:60000"}' --apply-to queues
通过以上命令,为所有的队列都设置了一个60s的消息有效期。
在声明队列时使用x-arguments设置消息的TTL
以下示例通过java代码,在创建队列时指定消息的TTL为60s:
Map<String, Object> args = new HashMap<>();
args.put("x-message-ttl", 60000);
channel.queueDeclare("myqueue", false, false, false, args);
当队列中已有消息时,通过policy为队列设置消息的TTL会带来警告。
当消息被重新入队时(比如 使用AMQP方法时指定了requeue参数为true,或者由于通道关闭),将会保留消息的原始过期时间。
将消息的TTL值设置为0,意味着消息到达队列后将会立即过期不会被队列保存,除非消息能够被立即传递给消费者。所以这给RabbitMQ不支持immediate的推送标记提供了另一种选择性。不像其他标记,当没有使用basic.return方法时,如果队列设置了死信交换机(dead letter exchange),则过期消息将会被路由到死信队列(与死信交换机绑定的队列称为死信队列)。
在生产者中设置消息的TTL
TTL可以基于消息逐个设置,在发送消息时使用AMQP的basic.publish方法指定expiration字段的值。
expiration字段值描述了TTL的毫秒时长,它和x-message-ttl有相同的限制。因此,expiration字段的值必须是个字符串,服务器将仅接收代表数字的字符串。
当队列和消息同时设置了TTL,那么服务器将会选择其中较小的值。
以下示例使用java客户端,在发送消息时设置消息的有效期最多为60s:
byte[] messageBodyBytes = "Hello, world".getBytes();
AMQP.BasicProperties properties = new AMQP.BasicProperties.Builder()
.expiration("60000").build();
channel.basicPublish("my-exchange", "routing-key", properties, messageBodyBytes);
注意事项
当队列中已有消息,并设置消息的TTL,那么会追溯设置消息的有效期,当在特定情况下将会丢弃这些消息。只有当过期消息到达队列的头部时,它们才会被真实地丢弃(或死信路由)。消费者将不会接收到过期的消息,但是消息的传递和有效期有天然的竞争关系。比如,一个消息在被写入到socket但还未到达消费者时过期。
当设置了TTL过期时间的消息,可以在未过期消息后面排队,直到后者被消费或者过期。因此,被过期消息使用的资源并不会被释放,它们也会被队列的统计记入(比如,队列中消息的数量)。
当回溯地设置消息的TTL策略时,建议有消费者同时在线,可以保证消息的尽快丢弃。
考虑到给已存在的队列设置消息的TTL,来实现删除消息释放资源。设置队列的TTL(或队列清空,队列删除)应该替代这种方式。
队列的TTL
TTL也可以给队列本设置,不仅仅是队列内容。队列将在不被使用(比如,没有消费者)后的一段时间内过期。这个特性可以和队列的自动删除属性(auto-delete queue property)一块使用。
队列的TTL可以在队列声明时指定x-expires字段值进行设置,或者通过设置policy的expires参数值进行设置。该值决定了队列不再被使用后直到被自动删除的时长。不再使用的意思是队列没有订阅的消费者,队列最近没有被重新声明,并且在过期时间basic.get方法没有被调用。这个特性是十分有用的,比如,在通过RabbitMQ实现RPC调用时,会生成大量的回复队列。
服务器保证如果队列在最近的过期时间内没有被使用,那么该队列将会被删除。但是不保证在过期后能够以多快的速度删除。当服务器重启时,队列的租期重新开始计算。
参数x-expires的值或者policy的expires参数的值表示了过期时间的毫秒时长。它必须是一个正整数(不像消息的TTL可以设置为0)。值为1000表示消息不再被使用后1s将会被删除。
使用policy设置队列的TTL
设置队列不再使用后30分钟过期:
rabbitmqctl set_policy expiry ".*" '{"expires" : 1800000}' --apply-to queues
在声明时使用x-arguments设置队列的TTL
Map<String, Object> args = new HashMap<>();
args.put("x-expires", 1800000);
channel.queueDeclare("myqueue", false, false, false, args);
原文地址:https://www.rabbitmq.com/ttl.html