R2DBC Pool等待队列请求取消后连接未释放问题排查
根因分析
你遇到的连接泄漏问题本质是取消信号和连接初始化流程的竞态条件:
- 当请求已经完成
preQuery执行、连接已经从池内借出的瞬间,订阅取消信号刚好到达,此时Spring Data R2DBC 2.3.x版本的连接管理逻辑没有覆盖这种临界场景,不会触发connection.close()将连接归还池,导致连接永久处于被占用状态。 - 少量被取消的线程仍能执行业务逻辑是响应式流的正常特性:取消信号是异步传播的,若业务逻辑已经触发执行,取消信号无法立即终止已经启动的流程。
修复方案
1. 自定义连接工厂加兜底释放逻辑(最快生效)
在你现有的TestConnectionFactory的preQuery方法中增加doOnDiscard逻辑,主动回收被丢弃的连接。该逻辑仅当连接被取消且未被下游消费时触发,不会影响正常业务流程:
private Mono<Connection> preQuery(Connection connection) { return Mono.from(connection .createStatement("SELECT 1;") // 实际场景为SET SCHEMA租户选择逻辑 .execute()) .doOnCancel(() -> log.info("cancel during preQuery")) .thenReturn(connection) // 兜底回收逻辑 .doOnDiscard(Connection.class, conn -> { if (!conn.isClosed()) { Mono.from(conn.close()).subscribe(); log.info("discarded connection closed automatically"); } }); }
2. 配置连接池兜底回收策略
在配置文件中开启泄漏检测和连接最大存活时间,就算出现偶发泄漏也能自动回收:
spring: r2dbc: pool: max-size: 2 leak-detection: DEBUG # 开发环境可以开DEBUG,生产环境开WARNING即可 max-life-time: 30m # 连接最长存活30分钟,到期自动销毁回收
3. 升级依赖到稳定修复版本
你当前测试的r2dbc-pool 0.9.0.M1是里程碑版本,相关问题没有完全修复,建议将整套依赖升级:
- Spring Boot升级到
2.6.14+版本,对应Spring Data R2DBC官方修复了取消场景下的连接泄漏问题 - r2dbc-pool升级到
0.9.3.RELEASE及以上正式版本
4. 优化并发请求限流
你当前用Mono.when同时发起100个请求,远超过连接池maxSize=2的限制,会产生大量排队请求,放大取消竞态的概率。可以调整为带并发限制的批量请求,并发数和连接池maxSize保持一致,减少排队取消的场景。
内容的提问来源于stack exchange,提问作者StephaneMarc
相关产品推荐
相关产品推荐

