Spring Boot中SseEmitter多次刷新致Hikari连接池耗尽问题解决
解决SseEmitter导致Hikari连接池耗尽的问题
你的问题核心是长连接场景下的数据库连接泄漏——即便调用了SseEmitter.complete(),仍有连接没被正确归还给Hikari池,最终导致池被占满。下面是具体的排查和修复方案:
1. 别让SseEmitter绑定的线程长期持有数据库连接
如果你的推送逻辑里有数据库操作,必须保证连接用完就还,不能跟着SseEmitter的长连接一直占着:
- 用
try-with-resources包裹JDBC操作,确保连接自动释放:try (Connection conn = dataSource.getConnection()) { // 执行数据库查询/更新 } catch (SQLException e) { // 日志报错 } - 绝对不要给SseEmitter的生成或推送方法加
@Transactional注解——声明式事务会让连接绑定到线程上,直到事务结束,而SseEmitter的长连接会拖垮整个事务,导致连接一直被占用。 - 利用SseEmitter的回调强制清理连接:
emitter.onCompletion(() -> { // 手动释放当前线程绑定的Spring管理连接 Connection conn = DataSourceUtils.getConnection(dataSource); try { if (!conn.isClosed()) { DataSourceUtils.releaseConnection(conn, dataSource); } } catch (SQLException ignore) {} }); emitter.onTimeout(() -> { emitter.complete(); // 超时场景同样清理连接 // 重复上述释放逻辑 });
2. 调整Hikari配置,让它主动回收闲置连接
检查你的Hikari配置,这些参数可能是问题所在:
maximumPoolSize:如果每个页面刷新都会创建新的SseEmitter,且每个Emitter对应一个数据库连接,这个值要设得比预期的并发长连接数大一点,比如20-30(根据你的服务器配置)。idleTimeout:把闲置连接超时时间改短,比如设为30秒(30000),让Hikari主动回收没人用的连接。leakDetectionThreshold:开启连接泄漏检测,设为2秒(2000),这样Hikari会在日志里打印泄漏连接的调用栈,直接帮你找到哪段代码没释放连接:spring.datasource.hikari.maximum-pool-size=20 spring.datasource.hikari.idle-timeout=30000 spring.datasource.hikari.leak-detection-threshold=2000
3. 主动清理失效的SseEmitter实例
页面刷新后,旧的SseEmitter已经没用了,但如果没被正确移除,可能还在占用资源。维护一个全局的Emitter集合,创建新实例时先干掉旧的:
// 用ConcurrentHashMap保证线程安全 private final ConcurrentHashMap<String, SseEmitter> userEmitters = new ConcurrentHashMap<>(); public SseEmitter createUserEmitter(String userId) { // 移除并关闭用户的旧Emitter SseEmitter oldEmitter = userEmitters.remove(userId); if (oldEmitter != null) { try { oldEmitter.complete(); } catch (Exception e) { log.error("Failed to close old emitter for user {}", userId, e); } } // 创建新的Emitter,设置超时时间(比如30分钟) SseEmitter newEmitter = new SseEmitter(1800000L); userEmitters.put(userId, newEmitter); // 回调触发时自动移除 newEmitter.onCompletion(() -> userEmitters.remove(userId)); newEmitter.onTimeout(() -> { userEmitters.remove(userId); newEmitter.complete(); }); return newEmitter; }
这样能避免旧的Emitter一直挂着,连带占用数据库连接。
4. 监控连接池状态,验证修复效果
用Spring Boot Actuator的/actuator/hikaricp端点查看连接池状态,重点看activeConnections:如果刷新页面后,活跃连接数能回到正常水平,说明修复有效;如果还是持续增长,结合泄漏检测的日志,定位到具体的代码位置。
内容的提问来源于stack exchange,提问作者Hristo.L
相关产品推荐
相关产品推荐

