RabbitMQ 消息过期 TTL

TTL(Time To Live,生存时间)让消息在队列里存活指定时长后自动过期,是构建"临时凭证、定时失效"类业务的基础能力。RabbitMQ 的过期分消息级与队列级两种,单位都是毫秒。

消息级 TTL

发布时给单条消息设置 expiration 属性,只对这条消息生效,过期从消息进入队列那一刻起算:

import pika
ch = pika.BlockingConnection(pika.ConnectionParameters("127.0.0.1")).channel()
props = pika.BasicProperties(expiration="60000")   # 60 秒后过期
ch.basic_publish(exchange="", routing_key="q.verify",
                 body=b"code:889966", properties=props)

每条消息可设不同 TTL,适合同一队列中存活时长各不相同的消息。

队列级 TTL(x-message-ttl)

声明队列时指定 x-message-ttl,则该队列中所有消息统一按此时长过期;该参数在队列有消息后无法修改,只能删队列重建:

# 声明队列:队列内所有消息 60 秒过期
rabbitmqadmin declare queue name=q.ttl60 arguments='{"x-message-ttl":60000}'

消息级与队列级 TTL 都设置时,取两者中的较小值。

设置方式位置作用对象改动成本
expiration 属性消息属性单条消息每条可不同,无需重建
x-message-ttl队列参数队列内全部消息需删队列重建

队列自身过期(x-expires)

x-expires 不是消息过期,而是"队列空闲自动删除":队列持续指定毫秒没有被使用(无消费者、无 basic.get 取消息、无新消息入队等)就整队列删除,其中的消息随之丢失。典型用于临时队列自清理,避免长期残留:

# 队列 5 分钟无人使用即自动删除
rabbitmqadmin declare queue name=q.tmp arguments='{"x-expires":300000}'

过期机制与坑

RabbitMQ 只在队头检查过期:消息到达队头时才被判定过期并清除,队头之后的消息即使已过期也仍占着队列,等轮到它才被清理。

由此带来最常见的坑:先入队的长 TTL 消息堵在队头,会让排在后面本应更早过期的短 TTL 消息迟迟不过期。例如先发一条 10 分钟过期的消息,再连发 1 分钟过期的消息,后者要等队头清空后才开始被清理,实际失效时间远晚于预期。

另外,已过期但未轮到清理的消息仍会计入队列长度与 ready 数,监控里看到队列"有消息"可能是已过期的残影。

这条特性决定了消息级 TTL 的过期并不精确,对过期精度敏感的业务应考虑死信/延迟队列方案或应用侧自行计时。

典型场景:验证码过期

登录/注册验证码常用"5 分钟有效":发送时把验证码消息设好 TTL,即使消费者延迟处理,队列里的旧验证码也会因过期自动清除,避免把过期验证码继续投给用户。

类似场景还有短期 token、限时优惠券、临时通知的自动失效。

小结

TTL 单位毫秒:单条消息用 expiration 属性、整队列用 x-message-ttl、队列空闲自清理用 x-expires。记住"只在队头检查过期"的机制,设计时避免长 TTL 阻塞短 TTL,过期类业务即可放心使用。

笔记加载中…