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

两个OkHttp WebSocket连接其一出现线程饥饿,如何持续接收事件?

问题描述

我在Spring Boot应用中通过@Scheduled任务,同时使用两个独立的OkHttpClient实例与不同服务器建立WebSocket流连接。由于是独立实例,我认为OkHttpClient.Builder的连接池设置不会影响。两个流初始都能接收事件,但后续其中一个完全停止接收。我已打印工作线程哈希码确认它们是不同的。

使用命令行执行mvn run时问题同样存在,因此排除IntelliJ环境问题。

请问如何让两个Socket流持续接收事件?复用同一个OkHttpClient创建两个连接是否有帮助?

更新补充

为排查问题,我将代码拆分为两个独立应用,各自携带WebSocket监听器,但仍出现相同问题:其中一个启动数秒后停止接收回调。已排除远程服务器问题,因为单独运行任一应用都能持续接收回调流,我需要两个应用同时运行并互相通信。

额外说明:我使用两个Binance官方Java库分别订阅WebSocket流:

  • 第一个连接端点为wss://stream.binance.com:9443/ws/btcusdt@aggTrade
  • 第二个连接端点为wss://fstream.binance.com,通过发送JSON {"method":"SUBSCRIBE","id":1663348051361,"params":"btcusdt@aggTrade"}完成订阅

我已联系Binance客服,对方表示若单独订阅正常,无法解释同时运行时一个流停止的问题,无法提供进一步帮助。


解决方案建议

1. 复用同一个OkHttpClient实例

OkHttpClient设计为可复用的单例,每个实例会维护独立的线程池、连接池与调度器。多个实例可能引发线程资源竞争,尤其当两个库内部都有线程管理逻辑时,复用单例可统一管理资源,避免线程耗尽或调度冲突。

你可以创建全局OkHttpClient单例,传递给两个Binance库的客户端构建器(需确认库是否支持自定义OkHttpClient)。

2. 检查WebSocket心跳机制

Binance的WebSocket流需定期发送心跳包维持连接活跃,可能其中一个流的心跳逻辑未正常工作,导致服务器主动断开连接:

  • 查看两个库的源码,确认是否自动处理心跳。部分库可能默认开启心跳,另一些需手动配置。
  • 手动添加心跳逻辑:定时向服务器发送PING帧,或按照Binance规范发送{"method":"PING","id":123}这类心跳消息,并监听PONG响应。

3. 排查线程阻塞或死锁

即便工作线程哈希码不同,仍可能存在线程阻塞情况:比如监听器回调方法中执行耗时操作(同步IO、锁等待),导致线程无法处理后续WebSocket消息。

  • 回调方法内避免耗时操作,将业务逻辑剥离到独立线程池处理。
  • 使用VisualVM、JProfiler等工具查看线程状态,确认是否有线程处于BLOCKED或WAITING状态。

4. 统一配置连接超时参数

两个OkHttpClient实例的超时设置可能不一致,或默认超时过短导致连接被主动关闭。可统一配置:

OkHttpClient okHttpClient = new OkHttpClient.Builder()
    .connectTimeout(30, TimeUnit.SECONDS)
    .readTimeout(0, TimeUnit.SECONDS) // WebSocket通常设为0表示无超时
    .writeTimeout(0, TimeUnit.SECONDS)
    .build();

5. 捕获并记录WebSocket错误事件

确保WebSocket监听器实现onFailure、onClosing方法,记录详细错误信息(断开原因、异常栈跟踪),帮助定位是服务器主动断开还是客户端内部错误:

@Override
public void onFailure(WebSocket webSocket, Throwable t, Response response) {
    log.error("WebSocket连接失败: {}", response != null ? response.message() : t.getMessage(), t);
}

@Override
public void onClosing(WebSocket webSocket, int code, String reason) {
    log.warn("WebSocket正在关闭,代码: {}, 原因: {}", code, reason);
}

6. 检查操作系统文件句柄限制

同时运行两个应用可能耗尽操作系统文件句柄(每个WebSocket连接占用一个句柄):

  • Linux系统下用ulimit -n查看当前限制,临时调高(如ulimit -n 65535)测试是否解决问题。
  • 长期解决方案是修改系统文件句柄配置(如/etc/security/limits.conf)。

内容的提问来源于stack exchange,提问作者Kannan J

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.19 11:35:19