摘要
在量化程序、行情监控平台开发过程中,金融类 WebSocket 长连接普遍存在无心跳保活、断线不会自动重连、行情数据解析容错差等问题。本文以恒指期货为演示标的,使用 java-websocket 框架搭建一套可直接复用的客户端,内置 10 秒定时心跳、3 秒延迟重连、完整 JSON 异常捕获逻辑,适配股指、贵金属、外汇等各类行情场景。 关键词:Java WebSocket;金融行情;恒指期货;心跳重连;实时数据推送
一、开发痛点梳理
- 空闲无心跳,链路被服务端主动断开,无人知晓;
- 网络波动断连后无重试机制,需要手动重启服务;
- 行情 JSON 字段较多,未做异常判断,非法报文直接造成程序崩溃;
- 多品种订阅逻辑未封装,切换标的重复编写代码。
二、项目依赖 Maven
<dependency>
<groupId>org.java-websocket</groupId>
<artifactId>java-websocket</artifactId>
<version>1.5.4</version>
</dependency>
<dependency>
<groupId>com.alibaba.fastjson2</groupId>
<artifactId>fastjson2</artifactId>
<version>2.0.32</version>
</dependency>
三、完整可运行客户端代码
import org.java_websocket.client.WebSocketClient;
import org.java_websocket.handshake.ServerHandshake;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import java.net.URI;
import java.util.Timer;
import java.util.TimerTask;
/**
* 恒指期货实时行情订阅客户端
*/
public class HsiMarketClient {
// 行情推送真实地址(仅代码内配置使用)
private static final String WS_URL = "ws://39.107.99.235/ws";
// 恒指期货订阅编码
private static final String SYMBOL = "HX_HSI";
private static WebSocketClient webSocketClient;
private static Timer heartTimer;
// 心跳间隔10秒
private static final int HEART_CYCLE = 10000;
// 断连重连等待3秒
private static final int RETRY_WAIT = 3000;
public static void main(String[] args) {
connect();
}
public static void connect() {
try {
webSocketClient = new WebSocketClient(new URI(WS_URL)) {
@Override
public void onOpen(ServerHandshake handshake) {
System.out.println("行情长连接建立完成,执行品种订阅");
// 订阅报文标准格式
String subJson = "{\"Key\":\"" + SYMBOL + "\"}";
send(subJson);
startHeartBeat();
}
@Override
public void onMessage(String message) {
try {
JSONObject root = JSON.parseObject(message);
if (!root.containsKey("body")) {
return;
}
JSONObject data = root.getJSONObject("body");
System.out.printf(
"品种:%s 现价:%s 涨跌:%s 涨跌幅:%s%% 更新时间:%s%n",
data.getString("StockCode"),
data.get("Price"),
data.get("Diff"),
data.get("DiffRate"),
data.getString("Time")
);
} catch (Exception e) {
System.err.println("行情数据解析异常:" + e.getMessage());
}
}
@Override
public void onClose(int code, String reason, boolean remote) {
System.out.printf("连接断开,等待%d毫秒后自动重试%n", RETRY_WAIT);
if (heartTimer != null) heartTimer.cancel();
new Thread(() -> {
try {
Thread.sleep(RETRY_WAIT);
connect();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}).start();
}
@Override
public void onError(Exception ex) {
System.err.println("WebSocket通信异常:" + ex.getMessage());
}
};
webSocketClient.connect();
} catch (Exception e) {
System.err.println("连接初始化失败:" + e.getMessage());
}
}
// 启动定时心跳任务,每10s发送一次ping报文
private static void startHeartBeat() {
heartTimer = new Timer();
heartTimer.scheduleAtFixedRate(new TimerTask() {
@Override
public void run() {
if (webSocketClient != null && webSocketClient.isOpen()) {
long timestamp = System.currentTimeMillis() / 1000;
String pingMsg = "{\"ping\":" + timestamp + "}";
webSocketClient.send(pingMsg);
}
}
}, 0, HEART_CYCLE);
}
}
四、行情返回核心字段说明
| 字段 | 释义 |
|---|---|
| StockCode | 交易品种唯一编码 |
| Price | 实时最新成交价 |
| Open | 当日开盘价格 |
| LastClose | 上一交易日收盘价 |
| High | 日内最高价格 |
| Low | 日内最低价格 |
| Diff | 涨跌数值 |
| DiffRate | 涨跌幅百分比 |
| BP1 | 一档买盘价格 |
| SP1 | 一档卖盘价格 |
| Time | 行情数据更新时间 |
五、补充:HTTP 同步拉取行情方式
如果业务不需要毫秒级实时推送,可采用 HTTP 接口定时拉取行情数据,请求头必须携带Accept-Encoding: gzip压缩参数,降低网络传输消耗。
import java.net.HttpURLConnection;
import java.net.URL;
import java.io.BufferedReader;
import java.io.InputStreamReader;
public class MarketHttpDemo {
public static void main(String[] args) throws Exception {
// 实时行情接口(仅代码内填写完整地址)
URL url = new URL("http://39.107.99.235:1008/getQuote.php?code=HX_HSI");
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestProperty("Accept-Encoding", "gzip");
BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream()));
String line;
while ((line = br.readLine()) != null) {
System.out.println(line);
}
br.close();
conn.disconnect();
}
}
六、历史 K 线数据获取示例代码
需要历史行情做指标计算、走势分析时,调用 K 线接口获取多周期数据,支持 1 分钟、5 分钟、1 小时、日线等周期。
import java.net.HttpURLConnection;
import java.net.URL;
import java.io.BufferedReader;
import java.io.InputStreamReader;
public class KlineDemo {
public static void main(String[] args) throws Exception {
// K线接口完整地址仅写在代码中
URL url = new URL("http://39.107.99.235:1008/redis.php?code=HX_HSI&time=1h&rows=200");
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestProperty("Accept-Encoding", "gzip");
BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream()));
String line;
while ((line = br.readLine()) != null) {
System.out.println(line);
}
br.close();
conn.disconnect();
}
}
七、线上生产环境优化建议
- 增加重连次数阈值,避免极端网络环境无限循环创建连接;
- 对接缓存或数据库,将实时行情持久化存储,用于历史复盘;
- 增加价格波动告警逻辑,当涨跌幅度超过阈值输出日志告警;
- 多品种并行订阅场景,封装工具类统一管理多个 WebSocket 连接;
- 服务部署使用守护进程托管,防止终端关闭导致程序退出。
八、总结
本文完整实现了适配恒指期货的行情 WebSocket 客户端,解决长连接心跳保活、自动断线重连、行情报文安全解析三大核心开发难点。这套代码逻辑通用,只需修改品种编码,即可快速迁移至外汇、贵金属、其他股指期货等各类行情业务场景。
776

被折叠的 条评论
为什么被折叠?



