发布订阅容器
RedisMessageListenerContainer 把 Redis 的订阅连接与业务 listener 解耦:一个 subscriber 负责收消息,task executor 负责分发。
先给答案:Pub/Sub 适合实时广播,不提供 Stream 那样的可回放消费进度
Section titled “先给答案:Pub/Sub 适合实时广播,不提供 Stream 那样的可回放消费进度”Pub/Sub 消息发送给当前在线订阅者,断线期间的消息不会自动补发;Stream 则把消息写入持久化结构,消费者可以通过 ID 和 group 记录进度、确认和待处理消息。
因此选择不是“哪个 API 更现代”,而是业务是否需要离线补偿、消费确认和重放。订阅连接通常要独占,不能像普通命令连接一样混用;处理器阻塞还会影响同一订阅连接上的后续消息。
Redis Pub/Sub connection -> Subscriber -> channelMapping / patternMapping -> taskExecutor -> MessageListener.onMessage类说明在 listener/RedisMessageListenerContainer.java:73-111:多个 listener 复用一个连接,消息分发交给 executor;连接仅在至少存在 listener 时建立。
容器用 ConcurrentHashMap 保存 channel、pattern 和 listener 反向索引,见 :155-163。新增订阅时,如果已经 listening,会在 :706-731 立即发起 subscribe 并等待同步完成。
processMessage 位于 :839-854,只负责调用 listener 并捕获异常;handleListenerException 位于 :856-874,将异常交给 ErrorHandler 或记录日志。因此 listener 异常不会直接杀死订阅线程。
替代方案:每个 listener 建立一条 Redis 订阅连接。
为什么不行:连接和线程数量随 listener 数增长,且订阅连接本身是专用连接。
证据:类注释明确采用 one connection + multiplexed listeners,见 RedisMessageListenerContainer.java:77-90。
| 场景 | 现象 | 原因 | 规避 |
|---|---|---|---|
| 把 Pub/Sub 当可靠队列 | 消费者离线期间消息丢失 | Pub/Sub 无持久 pending list | 需要可靠消费时用 Stream |
| listener 阻塞 | 其他消息延迟 | 共用 task executor | listener 内部快速返回或隔离线程池 |
| 订阅连接断开 | 消费中断 | 驱动连接失效 | 配置 BackOff 和 ErrorHandler |
事件接收与业务分发可以拆成两个并发域:接收线程保持协议状态,执行线程承载不可信业务代码。索引要同时维护正向和反向关系,才能低成本增删订阅。
面试锚点
- 为什么多个 listener 可以复用一条连接?
- listener 异常如何避免影响订阅线程?
- Pub/Sub 和 Stream 的可靠性边界是什么?