必须用GetSubscriber()获取ISubscriber实例进行订阅,IDatabase.Publish()仅为伪方法,不参与Pub/Sub连接管理;订阅需在ConnectionMultiplexer.IsConnected为true后执行,且发布与订阅须共用同一连接实例,否则消息无法接收。

必须用 GetSubscriber() 获取订阅实例,IDatabase 不能订阅
很多人一上来就用 redis.GetDatabase().Publish(...) 试发布,发现能发成功,但死活收不到消息——因为 Publish() 在 IDatabase 上只是个“伪方法”,它底层调的是 COMMAND PUBLISH,但不参与 Pub/Sub 连接生命周期管理;真正建立订阅通道、维持客户端连接状态的,只有 IConnectionMultiplexer.GetSubscriber() 返回的 ISubscriber 实例。
关键点:
-
ISubscriber是轻量级、无状态的,每次调用GetSubscriber()都返回新实例,但背后共享同一个物理连接 - 订阅动作(
SubscribeAsync())必须在连接就绪后执行,即ConnectionMultiplexer.IsConnected == true - 发布和订阅若跨不同
ConnectionMultiplexer实例(比如两个单例类各自 new 一个连接),消息一定收不到——Redis 服务端只把消息推给“当前连接上已注册该频道的客户端”
SubscribeAsync() 后不 UnsubscribeAsync() 会导致内存泄漏
订阅不是一次性的。只要连接没断,ISubscriber 就会持续监听频道,且 Redis 服务端会为每个活跃订阅者维护一个 client 结构体(含缓冲区、回调队列等)。如果长期运行的服务(如 ASP.NET Core 后台服务)反复调用 SubscribeAsync() 却从不取消,最终会撑爆服务端连接数限制或客户端内存。
实操建议:
- 订阅应绑定到明确生命周期,比如
IHostedService.StartAsync()中订阅,StopAsync()中显式调用UnsubscribeAsync() - 不要在短生命周期对象(如 MVC Controller)里做长期订阅;如需临时监听,务必配对调用
UnsubscribeAsync() - 使用
using var sub = connection.GetSubscriber();不起作用——ISubscriber不实现IDisposable,UnsubscribeAsync()必须手动触发
频道名大小写敏感,且不自动 trim 空格
Redis 的频道是纯字符串键,PUBLISH mychannel "hi" 和 SUBSCRIBE MyChannel 完全不匹配;SUBSCRIBE "mychannel "(末尾带空格)也收不到 mychannel 的消息。这个细节在调试时极难察觉,尤其当频道名来自配置项或用户输入。
常见踩坑场景:
- AppSettings.json 中写
"ChannelName": " user.events ",代码直接拼接sub.Subscribe(channelName),实际订阅的是带空格的字符串 - 前端传来的 channel 参数未校验,如
POST /api/pub?channel=%20orders%20,后端未.Trim()就转发给 Redis - 多环境部署时,开发用小写
test,生产误配成TEST,消息静默丢失
模式订阅(PSUBSCRIBE)不支持正则,只支持 glob 风格通配符
想用 * 匹配任意字符、? 匹配单字符、[abc] 匹配字符集——可以;但别指望写 .* 或 \d+,Redis 原生不支持 PCRE/ECMAScript 正则。它的模式匹配逻辑由服务端 stringmatchlen() 函数实现,属于简单 glob 比对。
这意味着:
-
psubscribe news.*能匹配news.sports、news.tech,但不匹配news_sports(.不是通配符) -
psubscribe users:*可以,但psubscribe users:\d+会当作字面量频道处理,不会生效 - C# 中调用
sub.PSubscribeAsync(RedisChannel)时,RedisChannel构造必须指定RedisChannel.Pattern类型,否则会被当成普通频道
真实项目中,过度依赖模式订阅容易让频道设计失控。建议优先用明确频道(如 orders.created、orders.updated),仅在确实需要动态分组时(如租户隔离:tenant:123:events)才启用模式。










