怎么实时监听Redis过期事件,订阅提醒那块怎么搞比较方便
- 问答
- 2025-12-29 16:13:34
- 3
要实时监听Redis的过期事件,并在过期时收到提醒,核心思路是开启Redis本身的一个叫做“键空间通知”的功能,然后让你的应用程序像一个订阅者一样,去订阅这个频道,等着接收Redis发过来的消息。
第一部分:让Redis能够发出通知(配置Redis)
你得告诉Redis:“嘿,请你在有键过期的时候,发个消息通知我。” 这个功能默认是关闭的,因为开启它会消耗一点点CPU资源,你需要修改Redis的配置文件(通常是 redis.conf)。
在配置文件里,找到关于 notify-keyspace-events 的配置项,它可能被注释掉了(行首有个#号),你需要把它改成类似下面的样子:
notify-keyspace-events Ex
这里解释一下这几个字母的意思(虽然你拒绝专业化术语,但这个简单了解有助于你理解):

- E 代表开启键空间事件通知。
- x 代表只关心过期事件(expired)。
你也可以用更简单的 notify-keyspace-events AKE,这里的 A 代表所有事件,K 代表键空间事件,E 代表键事件,这样配置后,不仅仅是过期,任何对键的增删改操作都会通知,信息量会比较大,对于只关心过期事件来说,用 Ex 就足够了。
修改完配置后,你需要重启Redis服务,或者如果是在命令行里,可以用 CONFIG SET notify-keyspace-events Ex 这个命令来临时生效(重启后会失效,永久生效还是要改配置文件)。
根据一篇名为《Redis键空间通知实现延迟任务》的博客文章提到,这是实现监听的第一步,必须确保这个配置是正确的。
第二部分:让你的程序成为订阅者(编写客户端代码)
Redis配置好后,它就会像一个广播电台,现在你需要编写一个一直运行着的程序,像收音机一样调到正确的频道,等着收听过期消息。

这个“频道”在Redis里有固定的命名规则,对于过期事件,频道名是 __keyevent@0__:expired,这里的 0 指的是第0号数据库(Redis默认有16个数据库,从0开始),如果你的键在其他数据库,需要相应修改。
用你熟悉的编程语言来实现这个订阅者,这里用Python语言举例,因为它比较易懂。
你需要一个Redis客户端库,redis-py。
import redis
import time
# 连接到Redis服务器
r = redis.Redis(host='localhost', port=6379, db=0, decode_responses=True)
# 创建一个订阅对象,并订阅特定的频道
pubsub = r.pubsub()
pubsub.psubscribe('__keyevent@0__:expired') # 使用psubscribe可以进行模式匹配,更灵活
print("开始监听Redis键过期事件...")
# 持续监听消息
for message in pubsub.listen():
# message是一个字典,包含频道、模式、数据等信息
if message['type'] == 'pmessage': # 如果是模式订阅收到的消息
# 过期的那个键的名字,就在 message['data'] 里
expired_key = message['data']
print(f"检测到键过期了: {expired_key}")
# 在这里编写你的业务逻辑,比如发邮件、发微信通知、调用另一个接口等等
# if expired_key.startswith("order_"): send_order_timeout_alert(expired_key)
这段代码做的事情是:
- 连接Redis。
- 创建一个订阅对象。
- 订阅了过期事件的频道,这里用了
psubscribe(模式订阅),它允许使用通配符,虽然这里频道名是固定的,但这是个好习惯。 - 然后进入一个无限的循环
pubsub.listen(),这个循环会一直阻塞,直到有消息到来。 - 当有键过期时,Redis会推送消息到这个循环里,我们就能从
message['data']中拿到那个过期的键的名字。
第三部分:怎么搞比较方便和一些注意事项

-
确保订阅程序的稳定性:这个订阅程序必须7x24小时不间断运行,如果它挂掉了,那么在它宕机期间发生的所有过期事件你就都收不到了,你需要把它部署在一个可靠的环境里,比如用Docker容器管理,或者用Supervisor这类进程守护工具,确保它崩溃后能自动重启。
-
处理消息的幂等性:所谓幂等性,简单说就是同一条消息你处理一次和处理多次的结果应该是一样的,因为网络问题或者你的程序问题,有可能同一条过期通知你可能会收到多次,在你的处理逻辑里(
print(f"检测到键过期了: {expired_key}")下面那部分),最好要考虑到这一点,根据过期的键ID去数据库检查状态,如果已经是“已过期”状态,就不要再重复处理了。 -
事件不是精确的:Redis的过期事件不是完全精确到秒的,Redis的过期策略是惰性删除和定期删除结合,意思是,一个键到期后,不一定马上就会被Redis删除并发出通知,可能会有一个微小的延迟,如果你的业务场景要求秒级的精确度,这个方案可能有一点点风险,但对于大多数提醒类场景(比如订单30分钟未支付关闭),这个延迟是完全可接受的,根据Redis官方文档对过期时间的解释,这是由其内部机制决定的。
-
消息里只有键名:非常重要的一点是,过期事件消息里只包含那个过期的键的名字(Key),不包含这个键原来对应的值(Value),如果你需要在处理过期事件时用到原来的值,你有两个选择:
- 方案一:在设置这个键的时候,把必要的业务信息直接编码在键名里,不要设一个键叫
order_12345,而是设成order_12345_userId_67890_amount_100,这样过期时你从键名就能解析出需要的信息,但这样键名会很长。 - 方案二:在设置键的同时,把完整的值存到另一个不会过期的键里,设置
order:12345的同时,把订单详情存到order:detail:12345里,当order:12345过期时,你收到通知,再去order:detail:12345里取出详细信息进行处理,处理完再删除这个详情键。
- 方案一:在设置这个键的时候,把必要的业务信息直接编码在键名里,不要设一个键叫
-
选择成熟的库:无论你用Java(如Jedis、Lettuce)、Go(go-redis)、Node.js(ioredis)还是其他语言,都选择社区活跃、支持Pub/Sub功能的客户端库,它们的核心思想和上面的Python例子都是相通的。
最方便的做法就是:配置好Redis -> 写一个稳定运行的订阅程序 -> 在程序里根据收到的过期键名去执行你的提醒逻辑,只要注意了上面提到的稳定性和数据问题,这就是一个非常直接且有效的解决方案。
本文由帖慧艳于2025-12-29发表在笙亿网络策划,如有疑问,请联系我们。
本文链接:https://haoid.cn/wenda/70745.html
