必须启用键空间通知才能监听redis配置变更,否则订阅频道为空;需用config set或redis.conf开启notify-keyspace-events(如ex、eg等),频道名须严格匹配db号和键名;推荐使用redisson rtopic替代原生subscribe以保障可靠性、自动重连与事件解析。

订阅配置变更频道前必须启用键空间通知
Redis 默认不发送键变更事件,__keyspace@0__: 这类频道始终为空。必须显式开启键空间通知,否则 SUBSCRIBE __keyspace@0__:config:app 会一直阻塞、收不到任何消息。
启用方式有两种:
- 运行时临时开启:
CONFIG SET notify-keyspace-events "Ex"(E启用键事件,x启用过期事件;若还需监听 SET/DEL 等操作,补上g或$) - 持久化配置:在
redis.conf中添加notify-keyspace-events Ex,重启生效
注意:notify-keyspace-events 是位掩码组合,只开 Ex 不代表能监听所有写操作——比如 SET config:app "v2" 不会触发事件,除非你加了 g(通用事件)或 $(字符串事件)。
监听特定配置键需构造正确的键空间频道名
键空间通知通过两个固定频道发布事件:__keyspace@<db> 和 <code>__keyevent@<db>。监听配置变更,99% 场景应订阅前者,且频道名必须严格匹配数据库编号和键名。</db>
例如,你在 DB 0 执行 SET config:service_timeout 30000,对应频道是:__keyspace@0__:config:service_timeout。少一个冒号、错一位数字、DB 号不对,都收不到消息。
常见错误:
- 误写成
__keyspace@0:config:service_timeout(缺第二个下划线) - 误以为所有配置键可统一监听,试图
SUBSCRIBE __keyspace@0__:config:*—— Redis 不支持通配符订阅键空间频道,PSUBSCRIBE对这类内部频道无效 - 应用连接的是 DB 1,却监听
__keyspace@0__:频道
用 SUBSCRIBE 直连监听不如用 Redisson RTopic 封装可靠
原生命令 SUBSCRIBE __keyspace@0__:config:retry_count 虽然能收到事件,但存在三个硬伤:
- 连接断开后不会自动重连,
SUBSCRIBE是阻塞命令,异常退出即丢失监听 - 收到的只是原始事件字符串(如
"set"),需手动解析键名、事件类型、时间戳,容易出错 - 无法跨 JVM 实例做去重或幂等控制,多个服务实例同时监听同一键,可能重复执行刷新逻辑
推荐用 RedissonClient 的 RTopic 封装:
RTopic topic = redisson.getTopic("__keyspace@0__:config:timeout");
topic.addListener(String.class, (channel, msg) -> {
if ("set".equals(msg)) {
// 触发本地配置热更新
reloadTimeoutConfig();
}
});
它底层自动处理重连、序列化、线程安全回调,且支持分布式锁协调多实例行为。
生产环境必须区分“配置变更”和“配置删除”事件
同一个键的 DEL 和 SET 都会向 __keyspace@0__:config:xxx 发送消息,但事件内容不同:前者是 "del",后者是 "set"。忽略这个差异会导致严重问题——比如配置被误删后,系统仍按旧值运行,或反复尝试从空值恢复。
正确做法是明确判断事件类型:
- 收到
"set":加载新值,校验合法性,触发刷新 - 收到
"del":回退到默认值 or 抛异常告警,不能静默忽略 - 收到
"expired":说明配置键设置了 TTL 且已过期,等价于被删,处理同"del"
最容易被忽略的一点:键空间通知本身不保证顺序。如果连续执行 SET config:a 1 → DEL config:a,理论上可能先收到 "del" 再收到 "set"(极低概率,但网络分区或主从切换时真实存在)。关键配置同步场景,建议加一层本地版本号或时间戳比对。











