Redis中如何订阅指定频道的订阅者数量(避免轮询)
解决方案
要解决Redis中实时获取频道订阅者数量的问题——因为原生PUBSUB NUMSUB只能主动查询无法订阅变化,轮询又会浪费资源——你可以通过以下几种方案实现:
1. 客户端主动上报订阅/退订事件(最直接的实时方案)
Redis本身不会在客户端订阅/退订时触发通知,所以需要在业务层的订阅逻辑里额外添加两步原子操作,同时推送订阅数更新:
当客户端订阅目标频道(比如
chat:room1)时:# 原子增加订阅计数器 INCR chat:room1:subcount # 获取最新订阅数并推送到专门的更新频道 PUBLISH chat:room1:subcount_updated $(GET chat:room1:subcount)当客户端主动退订时:
# 原子减少订阅计数器 DECR chat:room1:subcount # 推送更新后的订阅数 PUBLISH chat:room1:subcount_updated $(GET chat:room1:subcount)
需要监控订阅数的服务,直接订阅chat:room1:subcount_updated频道,就能实时收到订阅数变化,完全无需轮询。
异常断开的处理
如果客户端意外断开(比如网络中断),Redis不会自动触发退订的计数器减操作,这时候需要做存活校验:
- 客户端订阅后,定期(比如每25秒)向Redis发送
SET client:sub:{用户ID} chat:room1 EX 30,用过期时间标记存活状态 - 后台启动定时任务(比如每分钟一次),用
SCAN遍历所有client:sub:*的键,找出已过期的条目,对对应频道执行DECR操作后推送更新。
2. 利用Redis键空间通知(Keyspace Notifications)
如果不想在客户端逻辑里加太多代码,可以通过Redis的键空间通知监听订阅计数器的变化:
先修改Redis配置,开启键空间通知:
# 开启所有键的事件通知(K=键空间事件,E=键事件,A=所有事件) CONFIG SET notify-keyspace-events KEA维护一个订阅计数器键(比如
chat:room1:subcount),客户端订阅/退订时仍需执行INCR/DECR更新这个键监控服务订阅对应键的变化事件:
# 订阅db 0中chat:room1:subcount键的所有变化事件 SUBSCRIBE __keyspace@0__:chat:room1:subcount当计数器变化时,监控服务会收到事件通知,此时再执行
GET chat:room1:subcount获取最新订阅数。
优缺点
- 优点:不需要修改太多客户端逻辑,依赖Redis原生通知机制
- 缺点:通知仅告知键发生变化,不会直接返回新值,需额外查询;键空间通知会产生一定额外流量,需根据实际场景评估性能。
3. 基于Redis Hash的在线客户端管理
如果需要更精准的在线用户统计,可以用Hash存储每个订阅频道的在线客户端:
客户端订阅时:
# 将用户ID加入频道的在线Hash,值设为当前时间戳 HSET chat:room1:online_users user123 $(date +%s) # 同时更新计数器并推送 INCR chat:room1:subcount PUBLISH chat:room1:subcount_updated $(GET chat:room1:subcount)客户端定时刷新存活状态:
HSET chat:room1:online_users user123 $(date +%s)后台定时清理超时用户:
# 获取所有在线用户的时间戳 HGETALL chat:room1:online_users # 遍历筛选出超时(比如超过30秒)的用户,逐个删除 HDEL chat:room1:online_users userXXX # 重新计算订阅数并推送 SET chat:room1:subcount $(HLEN chat:room1:online_users) PUBLISH chat:room1:subcount_updated $(GET chat:room1:subcount)
这种方法能更准确统计在线用户,尤其是需要区分用户是否真的在线的场景,但需要维护定时清理逻辑。
内容的提问来源于stack exchange,提问作者tobiasBora
相关产品推荐
相关产品推荐

