在量化交易系统或行情订阅程序中,WebSocket 是实现实时行情获取的关键通道。但在实际部署中,我们经常会遇到一个头痛的问题:WebSocket连接在运行一段时间后断开了,而我们的策略还以为数据一直在更新,直到发生实际交易损失。

本文将以外汇行情 WebSocket 接口为例,详细还原一次断连排查过程,并提供一个健壮的重连机制实现方案,帮助你构建高可用的实时行情接入组件。

初始接入代码示例(外汇行情)

根据infoway API的官方示例中,我们通过如下代码订阅了 BTCUSDT 的实时行情(注意:虽然标的是 crypto,Infoway 支持的外汇行情与加密币行情结构一致,实际只需修改订阅品种即可):

private static final String WS_URL = "wss://data.infoway.io/ws?business=forex&apikey=yourApikey";

// 申请免费token: https://infoway.io
// 对接文档:docs.infoway.io JSONObject jsonObject = new JSONObject();
jsonObject.put("code", 10000);
jsonObject.put("trace", UUID.randomUUID().toString());
JSONObject data = new JSONObject();
data.put("codes", "EURUSD"); // 示例:订阅 EUR/USD 外汇对
jsonObject.put("data", data); session.getBasicRemote().sendText(jsonObject.toJSONString());

这是典型的基于 @ClientEndpoint 的 WebSocket 接入方式。但在运行过程中,我们观察到 30 分钟左右后,控制台停止打印行情,连接虽然没有报错,但实际上已经断开了。

问题表现

  • 没有报错或异常(@OnError未触发);
  • @OnClose 也未打印;
  • 行情打印停止,ping 发送不报错但无回应;
  • 监控发现 WebSocket 实际已经被远端关闭(心跳丢失)。

原因分析

此类问题在接入 WebSocket 时较为常见,可能由以下原因之一引起:

  • 客户端网络瞬断;
  • 长时间未发送心跳或心跳机制失效;
  • 远端服务端定期断开空闲连接;
  • Session 对象未能正确检测“半连接”状态(即 TCP 已断但对象仍可调用);
  • Java WebSocket API 没有内建自动重连机制。

重连机制设计思路

为了解决这个问题,我们需要构建一个健壮的“自动重连机制”,核心包括:

监听连接状态:@OnClose 和 @OnError 都要触发重连;

  • 每次连接失败要有退避策略(避免死循环尝试);
  • 在主线程或守护线程中保持连接活跃监测;
  • 支持手动关闭与恢复机制,防止重复连接。

改造后的关键代码(支持自动重连)

package org.example.ws;

import com.alibaba.fastjson2.JSONObject;
import jakarta.websocket.*;
import java.io.IOException;
import java.net.URI;
import java.util.UUID;
import java.util.concurrent.*; // 申请免费token: https://infoway.io
// 对接文档:docs.infoway.io public class ReconnectableWSClient { private static Session session;
private static final String WS_URL = "wss://data.infoway.io/ws?business=forex&apikey=yourApikey";
private static final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(2);
private static final int RECONNECT_DELAY = 5; // seconds
private static volatile boolean userClose = false; public static void main(String[] args) {
connectWithRetry(); scheduler.scheduleAtFixedRate(ReconnectableWSClient::ping, 30, 30, TimeUnit.SECONDS);
} private static void connectWithRetry() {
scheduler.execute(() -> {
while (true) {
try {
WebSocketContainer container = ContainerProvider.getWebSocketContainer();
session = container.connectToServer(MyClientEndpoint.class, URI.create(WS_URL));
subscribeForex("EURUSD");
break;
} catch (Exception e) {
System.err.println("连接失败," + RECONNECT_DELAY + "秒后重试...");
try {
TimeUnit.SECONDS.sleep(RECONNECT_DELAY);
} catch (InterruptedException ignored) {}
}
}
});
} private static void subscribeForex(String symbol) throws IOException {
JSONObject jsonObject = new JSONObject();
jsonObject.put("code", 10000);
jsonObject.put("trace", UUID.randomUUID().toString());
JSONObject data = new JSONObject();
data.put("codes", symbol);
jsonObject.put("data", data);
session.getBasicRemote().sendText(jsonObject.toJSONString());
} private static void ping() {
if (session != null && session.isOpen()) {
try {
JSONObject jsonObject = new JSONObject();
jsonObject.put("code", 10010);
jsonObject.put("trace", UUID.randomUUID().toString());
session.getBasicRemote().sendText(jsonObject.toJSONString());
} catch (IOException e) {
System.err.println("Ping发送失败,尝试重连...");
reconnect();
}
}
} private static void reconnect() {
if (userClose) return;
try {
if (session != null && session.isOpen()) {
session.close();
}
} catch (IOException ignored) {}
connectWithRetry();
} @ClientEndpoint
public static class MyClientEndpoint {
@OnOpen
public void onOpen(Session session) {
System.out.println("连接已建立:" + session.getId());
} @OnMessage
public void onMessage(String message) {
System.out.println("收到数据:" + message);
} @OnClose
public void onClose(Session session, CloseReason reason) {
System.err.println("连接关闭,原因:" + reason);
reconnect();
} @OnError
public void onError(Session session, Throwable throwable) {
System.err.println("发生错误:" + throwable.getMessage());
reconnect();
}
}
}

实战经验总结

  • WebSocket 是实时行情系统的核心通道,必须做到可用性高、断线可恢复;
  • Java 的 WebSocket API 原生不支持重连,需要手动封装;
  • 合理使用线程池和状态控制,可以避免连接雪崩或资源泄露;
  • 实际部署中建议使用独立监控线程检测长时间无数据/心跳响应,进一步增强稳定性。

如何让外汇WebSocket连接不断线?的更多相关文章

  1. 使用wireshark抓包分析浏览器无法建立WebSocket连接的问题(server为Alchemy WebSockets组件)

    工作时使用了Websocket技术,在使用的过程中发现,浏览器(Chrome)升级后可能会导致Websocket不可用,更换浏览器后可以正常使用. 近日偶尔一次在本地调试,发现使用相同版本的Chrom ...

  2. 7.1.1.关闭WebSocket连接

    7.1.定义 7.1.1.关闭WebSocket连接 为_关闭WebSocket连接_,端点需关闭底层TCP连接.端点应该使用一个方法完全地关闭TCP连接,以及TLS会话,如果合适,丢弃任何可能已经接 ...

  3. 学习html5的WebSocket连接

    1.什么是WebSocket WebSocket 是一种自然的全双工.双向.单套接字连接.使用WebSocket,你的HTTP 请求变成打开WebSocket 连接(WebSocket 或者WebSo ...

  4. WebSocket 连接关闭(代码:1006)

    前端WebSocket 连接关闭(代码:1006) function connect() { //判断当前浏览器是否支持WebSocket if ('WebSocket' in window) { w ...

  5. Chrome_查看 webSocket 连接信息

    1.以下代码实现一个webSocket连接,在文本输入框中输入内容,点击发送,通过服务器,返回相同的内容显示在下方. <!DOCTYPE html> <html lang=" ...

  6. websocket连接的后台反向代理问题

    今天要介绍的问题,是一个相对来说比较经典的问题,问题表面看不是很复杂的问题,但是反映出的背后通信逻辑,其实还是比较有意义的. websocket协议是当前绝大部分浏览器都支持的长连接协议,是HTTP协 ...

  7. 具备双向通行能力的架构对于移动APP属于刚性需求。 WebSocket连接 注册信令

    双向通信使用指南_用户指南(开放 API)_API 网关-阿里云 https://help.aliyun.com/document_detail/66031.html 流程描述 (1) 客户端在启动的 ...

  8. .net , java webSocket 连接 Socket.io (1.4.4版本) 问题

    .net版Socketio4net类库和java版socket.io-java-client类库 连接socket.io 1.4版本都不行,网上大多是socket.io 0.9版本的,socket.i ...

  9. Chrome 浏览器中查看 webSocket 连接信息

      1.以下代码实现一个webSocket连接,在文本输入框中输入内容,点击发送,通过服务器,返回相同的内容显示在下方. 1 <!DOCTYPE html> 2 <html lang ...

  10. 【web】Chrome 浏览器中查看 webSocket 连接信息

    1.以下代码实现一个webSocket连接,在文本输入框中输入内容,点击发送,通过服务器,返回相同的内容显示在下方. 1 <!DOCTYPE html> 2 <html lang=& ...

随机推荐

  1. 【Win32】VC6 Visual C/C++ 6.0 修改程序图标

    零.需求 就想给自己的C程序加个图标,好看些 一.解决 1.操作步骤 1.新建一个资源脚本 2.在新建的脚本上右键,选择插入 3.选择Icon,点新建或者引入,如果你没有准备图标点新建,有的话直接点引 ...

  2. Python 类型检查与类型注解:mypy 与 typing 深度解析

    Python 类型检查与类型注解:mypy 与 typing 深度解析 在 Python 动态类型语言中,mypy 和 typing 是两个提升代码健壮性的核心工具.它们通过静态类型检查与类型注解,帮 ...

  3. Electron 开发:获取当前客户端 IP

    Electron 开发:获取当前客户端 IP 一.背景与需求 1. 项目背景 客户端会自启动一个服务,Web/后端服务通过 IP + port 请求以操作客户端接口 2. 初始方案与问题 2.1. 初 ...

  4. flowable任务监听器和java-service依赖注入问题

    前言 1. Flowable中Java服务任务的依赖注入方法,比如使用Spring的@Autowired或构造函数注入,并确保服务任务类由Spring管理. 2. 流程引擎配置中启用依赖注入和表达式解 ...

  5. DPDI(Dispatch PDI)kettle调度管理平台升级内容

    DPDI升级内容(20240815版) DPDI online部署方式 定时任务优化(支持轮询机制,Cron可提示近5次运行时间) 运行任务优化(支持多机器分布式运行) 其它小功能优化 1. 首页可手 ...

  6. Linux内核模块开发(简单)

    Linux系统为应用程序提供了功能强大且容易扩展的API,但在某些情况下,这还远远不够.与硬件交互或进行需要访问系统中特权信息的操作时,就需要一个内核模块. Linux内核模块是一段编译后的二进制代码 ...

  7. K8s容器运行时,移除Dockershim后存在哪些疑惑?

    K8s容器运行时,移除Dockershim后存在哪些疑惑? 大家好,我是秋意零. K8s版本截止目前(24/09)已经发布到了1.31.x版本.早在K8s版本从1.24.x起(22/05),默认的容器 ...

  8. GSLibrary平台本地搭建(windows)

    一.安装配置数据库 https://dev.mysql.com/downloads/windows/installer/5.7.html 创建GSLibrary数据库 mysql -uroot -p ...

  9. 端到端自动驾驶系统实战指南:从Comma.ai架构到PyTorch部署

    引言:端到端自动驾驶的技术革命 在自动驾驶技术演进历程中,端到端(End-to-End)架构正引领新一轮技术革命.不同于传统分模块处理感知.规划.控制的方案,端到端系统通过深度神经网络直接建立传感器原 ...

  10. 简单介绍下 Vue 2.x 中的几种生命周期钩子(Lifecycle Hooks)

    〇.前言 在使用 Element UI 框架(基于 Vue 2.x)开发应用时,理解 Vue 的生命周期钩子(Lifecycle Hooks)是非常重要的. 这些钩子函数可以在组件的不同阶段执行特定的 ...