两个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

