2、WebSocket基础:WebSocket协议原理、与HTTP的区别、心跳机制
各位同学,今天我们来聊聊WebSocket。这东西在量化交易里有多重要呢?我这么说吧,没有WebSocket,你的行情数据延迟可能比别人慢几百毫秒。在抢单的世界里,这几百毫秒足够让一笔交易从盈利变成亏损。
我个人习惯把WebSocket理解为「HTTP的升级版」。但严格来说,它俩根本不是一回事。咱们先看看协议原理。
2.1 WebSocket协议原理
WebSocket的设计初衷很简单:让客户端和服务器之间建立一个全双工的通信通道。什么叫全双工?就是两边可以同时说话,不用等对方说完。
我记得第一次接触WebSocket时,脑子里全是问号:「这不就是长连接吗?」后来踩了坑才明白,长连接和全双工是两码事。HTTP的长连接只是复用TCP连接,但通信模式还是「一问一答」。
WebSocket的握手过程很有意思。它借用了HTTP的升级机制:
- 客户端发一个HTTP GET请求,带上
Upgrade: websocket头 - 服务器返回101状态码,表示「协议切换」
- 之后双方直接走WebSocket帧,不再走HTTP协议
来看一个典型的握手请求:
GET /ws HTTP/1.1
Host: example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Sec-WebSocket-Version: 13
服务器返回:
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
嗯,这里要注意:Sec-WebSocket-Key 不是用来鉴权的,它只是一个随机数,用来证明服务器确实支持WebSocket。我见过有人在这上面加业务逻辑,结果把自己坑了。
核心要点:WebSocket握手只有一次HTTP请求,之后的数据传输不再走HTTP。这意味着你省去了每次请求的头部开销——对于高频行情来说,这能省下30%-50%的带宽。
2.2 与HTTP的区别
说白了,HTTP是「你问我答」模式,WebSocket是「随时开聊」模式。咱们用表格对比一下:
| 对比维度 | HTTP | WebSocket |
|---|---|---|
| 通信模式 | 半双工(请求-响应) | 全双工(双向实时) |
| 连接开销 | 每次请求都有头部(几百字节) | 握手后头部仅2-14字节 |
| 实时性 | 依赖轮询或长轮询 | 服务器主动推送 |
| 协议开销 | 高(Cookie、认证等) | 低(二进制帧) |
| 适用场景 | REST API、网页加载 | 行情推送、实时聊天 |
你想想看,如果用HTTP轮询行情,每秒请求一次,光头部开销就够你受的。我在做币安行情聚合时测试过,HTTP轮询的延迟在200-500ms,而WebSocket推送的延迟只有10-50ms。差了整整一个数量级。
避坑指南:我曾经在项目中用HTTP长轮询模拟实时推送,结果服务器连接数暴涨,最后把Nginx搞崩了。后来换成WebSocket,连接数从10万降到1万(因为复用),CPU占用率直接腰斩。
2.3 心跳机制
这是WebSocket里最容易出问题的地方。很多同学以为连接建立后就万事大吉了,结果半夜行情断了都不知道。
心跳机制说白了就是「定期确认对方还活着」。WebSocket协议本身提供了 Ping 和 Pong 帧:
- 客户端发
Ping帧 - 服务器必须回复
Pong帧 - 如果超时没收到,就认为连接断了
但实际项目中,很多交易所并不按规范来。比如币安的WebSocket,它要求客户端每3分钟发一次Ping,否则服务器会主动断开。而OKX则是服务器每15秒发一次Ping,客户端只需要回复Pong。
来看一个通用的心跳实现(Python伪代码):
import asyncio
import websockets
async def heartbeat(ws, interval=30):
"""每30秒发送一次Ping"""
while True:
try:
await ws.ping()
print(f"[{time.time()}] 发送Ping")
await asyncio.sleep(interval)
except websockets.ConnectionClosed:
print("连接已断开,停止心跳")
break
async def main():
async with websockets.connect("wss://stream.binance.com/ws") as ws:
# 启动心跳任务
asyncio.create_task(heartbeat(ws))
# 处理行情数据
async for msg in ws:
process_trade(msg)
重要警告:不要依赖WebSocket协议自带的Ping/Pong!很多代理服务器(比如Nginx、HAProxy)会吃掉这些帧。我建议在应用层自己实现心跳——也就是在业务消息里夹带一个「心跳消息」类型。这样即使协议层被代理干扰,你的应用层还能感知到连接状态。
我曾经踩过一个坑:用Kubernetes部署行情服务,WebSocket连接经常莫名其妙断开。查了两天才发现,是K8s的负载均衡器把空闲连接给回收了。解决方案就是在应用层加心跳,让连接一直处于「活跃」状态。
2.4 知识体系总览
下面这张图总结了WebSocket的核心知识结构,我建议你保存下来:
这张图里三个模块是环环相扣的。协议原理是基础,与HTTP的对比帮你理解「为什么选WebSocket」,心跳机制则是实战中的保命技能。我建议你按这个顺序学习,别跳着看。
个人经验:我在做多交易所行情聚合时,发现不同交易所的心跳策略完全不同。有的30秒,有的3分钟,有的甚至不主动发Ping。我的做法是写一个统一的心跳适配层,针对每个交易所配置不同的心跳参数。这样即使交易所改了策略,我只需要改配置文件,不用动代码。
好了,WebSocket的基础就讲到这里。记住一句话:协议原理决定上限,心跳机制决定下限。原理不懂,你写不出高性能代码;心跳不做,你的系统随时可能掉线。
公众号:蓝海资料掘金营,微信deep3321