RabbitMQ 学习系列8 进阶(过期、死信) 2
4.2 过期时间(TTL)
TTL,Time to Live的简称,即过期时间, Rabbitmq可以对消息和队列设置TTL。
4.2.1设置消息的TTL
目前有两种方法可以设置消息的TTL,第一种方法是通过队列属性设置,队列中所有消息都有相同的过期时间,第二种方法是对消息本身进行单独设置,每条消息的TTL可以不同。如果 两种方法一起使用,则消息的TTL以两者之间较小的那个数值为准。 消息在队列中的生存时间一但超过设置的TTL值时,就会变成"死信"(Dead Message),消费者将无法再收到该消息(这点不是绝对的, 可以参考4.3)
通过队列属性设置消息的TTL方法的arguments中加入x-message-ttl参数实现的,这个参数的单位是毫秒,如dic.add("x-message-ttl",6000)。
QueueDeclareOk QueueDeclare(string queue, bool durable, bool exclusive, bool autoDelete, IDictionary<string, object> arguments);
也可以通过rabbitmqctl 客户端命令或者 rabbitmq的api接口来设置队列的过期时间
如果不设置TTL,则表示此消息不会过期;如果将ttl设置为0,则表示除非此时可以直接将消息投递到消息者,否则该消息会被立即丢弃,这个特性可以部分替代rabbitmq 3.0版本之前的immediate参数,之所以部分代替,是因为immediate参数在投递失败时会用BasicReturn将消息返回(这个功能可以用死信队列来实现,参考4.3)
针对每条消息设置TTL的方法是在channel.basicPublish方法的IBasicProperties参数对象的Expiration属性设置,单位为毫秒.
void BasicPublish(string exchange, string routingKey, bool mandatory, IBasicProperties basicProperties, ReadOnlyMemory<byte> body);
对于第一种方法设置队列ttl属性的方法(通过队列属性设置),一但消息过期,就会从队列中抹去,而在第二种方法中(IBasicProperties参数对象的Expiration属性) ,即使消息过期,也不会马上从队列中抹去,因为每条消息是否过期是在即将投递到消费者之前判定的。
两种方式处理不一样,因为第一种方法里,队列中已过期的消息肯定在队列头部,Rabbitmq只要定期从队头开始扫描是否有过期的消息即可。第二种方法里,每条消息的过期时间不同,如果要删除所有过期消息势必要扫描整个队列,所以不如等到此消息即将被消费时再判定是否过期,如果过期再进行删除即可。
4.2.2 设置队列的ttl
上面是设置消息的tll, 这里是设置队列的ttl,在arguments参数对象中加入x-expires参数实现,这个参数的单位是毫秒,如dic.add("x-expires",6000)。
QueueDeclareOk QueueDeclare(string queue, bool durable, bool exclusive, bool autoDelete, IDictionary<string, object> arguments);
x-expires参数可以控制队列被自动删除前处于未使用的时间,未使用的意思是队列上没有任何的消费者,队列也没有被重新声明,并且在过期时间段内也未调用过BasicGet命令。
Rabbitmq会确保在过期时间到达后将队列删除,但是不保障删除的动作有多及时,在Rabbitmq重启后,持久化的队列的过期时间会被重新计算。
4.3 死信队列
DLX,全称为Dead-letter-exchange,可以称死信交换器,当消息在一个队列中变成死信(dead message)之后,它能被重新发送到另一个交换器中,这个交换器就是DLX,绑定DLX的队列就称之为死信队列。
消息变成死信一般是由以下几种情况:
1.消息被拒绝(BasicReject或 BasicNack)
2.消息过期
3.队列达到最大长度(当前队列存储容量以满)
DLX也是一个正常的交换器,和一般的交换器没有区别,它能在任何的队列上被指定,实际上就是设置某个队列的属性。当这个队列中存在死信时,rabbitmq就会自动地将这个消息重新发布到设置的dlx上去,进而被路由到另一个队列,即死信队列。
通过在channel.basicPublish方法的arguments中加入x-dead-letter-exchange参数来为这个队列添加DLX。如dic.add("x-dead-letter-exchange","dlx_change")。dlx_change是dlx交换器名称
//定义一个dlx交换器 channel.ExchangeDeclare("dlx_exchange", ExchangeType.Direct); //定列一个队列,为队列myqueue添加DLX channel.QueueDeclare("myqueue",false,false,false, new Dictionary<string, object>() { {"x-dead-letter-exchange","dlx_chchange" } });
上面创建了两个交换器exchange.normal和exchange.dlx,分别绑定队列queue.normal和queue.dlx。
在web管理页面可以看出,两个队列都被标记了"D",这个是durable的缩写,即队列持久化,queue.noraml这个队列还配置了ttl,dlx,dlk。其中dlk是指x-dead-letter-routing-key这个属性
以于RabbitMQ来说,DLX是一个非常有用的特性,它可以处理异常情况下,消息不能够被消费者正确消费(消费者调用了basicNack或者basicReject)而被置入死信队列中的情况,后续分析程序可以通过消费这个死信队列中的内容来分析当时所遇到的异常情况,进而可以改善和优化系统。