连接抽象与工厂
连接工厂不是简单的对象工厂,而是驱动客户端、拓扑资源和 Spring 生命周期之间的边界。
先给答案:ConnectionFactory 把连接生命周期从业务代码中收口
Section titled “先给答案:ConnectionFactory 把连接生命周期从业务代码中收口”业务不应该每次命令都手动创建、选择、关闭连接;ConnectionFactory 根据客户端实现提供连接、共享连接、连接池和事务绑定。Lettuce 与 Jedis 的线程模型不同,但上层通过统一接口暴露连接操作。
“拿到连接”也不等于“可以在任何线程和模式下使用”。订阅连接、事务连接和普通命令连接有不同约束;排查连接泄漏或命令错乱时要看连接来源、是否绑定线程/事务以及释放路径。
RedisConnectionFactory + getConnection() + getClusterConnection() + getSentinelConnection() | +------+----------------+ | |LettuceConnectionFactory JedisConnectionFactory | |client/provider pool/client/topology接口在 connection/RedisConnectionFactory.java:27-68,还要求实现 getConvertPipelineAndTxResults(),把驱动原始结果是否转换纳入统一契约。
Lettuce 生命周期
Section titled “Lettuce 生命周期”LettuceConnectionFactory#start 在 connection/lettuce/LettuceConnectionFactory.java:922-948 使用状态 CAS 防止重复启动,创建 client、普通 provider、响应式 provider,并在 Cluster 模式创建 ClusterCommandExecutor。stop 位于 :963-984,按连接、provider、client 顺序释放。
getConnection 位于 :1046-1062:先断言已启动,Cluster 返回 Cluster 连接,否则从 shared connection/provider 构造 LettuceConnection。
Jedis 生命周期
Section titled “Jedis 生命周期”JedisConnectionFactory#start 位于 connection/jedis/JedisConnectionFactory.java:771-805,按 unified client、pool、Cluster 三种路径创建资源。getConnection 位于 :1001-1016,统一处理 Cluster、Unified Jedis 和 legacy Jedis 三种模式。
替代方案:每次命令都直接新建驱动连接。
为什么不行:连接握手和认证成本高,事务、Pub/Sub 又要求连接具有会话粘性。
证据:工厂将 getConnection 与 start/stop 分开,Lettuce 还维护 provider 和 shared connection,见 LettuceConnectionFactory.java:932-947。
| 场景 | 现象 | 原因 | 规避 |
|---|---|---|---|
| 把 Pub/Sub 连接用于普通命令 | 命令失败或阻塞 | 订阅连接进入专用模式 | 使用 listener container 管理 |
| Cluster 仍假设单连接 | MOVED/事务异常 | Cluster 需要节点路由 | 使用 Cluster API 和 hash slot 约束 |
| 忽略 stop | 线程、连接泄漏 | client/provider 未释放 | 让 Bean 实现 DisposableBean |
工厂接口应暴露稳定能力,不暴露驱动对象;生命周期对象应把创建和销毁对称实现,并在状态转换处防止重复启动。
面试锚点
- Lettuce 与 Jedis 工厂的共同契约是什么?
- 为什么 Cluster 连接不能简单复用普通连接?
start和stop如何保证资源对称?