You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.29 12:12:01