在现代交易所或跟单交易系统(Copy Trading)中,成交消息从撮合引擎传播到跟单系统通常不会直接依赖单一方式,而是采用分层架构:
text
撮合引擎
↓
成交回报模块(Trade Gateway)
↓
消息总线 / MQ(Kafka、Pulsar、RocketMQ等)
↓
跟单服务(Copy Trade Service)
↓
生成跟单订单
↓
订单网关
↓
TCP连接
↓
撮合引擎
撮合引擎内部一般使用 TCP
撮合引擎本身属于超低延迟系统:
- Order Gateway → Matching Engine
- Matching Engine → Trade Gateway
通常采用:
- TCP
- FIX协议
- 私有二进制协议
- Aeron
- Chronicle Queue
- 共享内存(同机房)
这里追求的是:
- 微秒级延迟
- 顺序保证
- 不丢消息
例如:
text
带单员下单
↓
Order Gateway
↓ TCP
Matching Engine
↓
成交
成交后通知跟单系统一般通过消息中间件
成交产生后:
json
{
"tradeId": 123456,
"userId": 10001,
"symbol": "BTCUSDT",
"side": "BUY",
"qty": 1,
"price": 65000
}
这条消息会被发送到:
- Kafka
- Pulsar
- RocketMQ
- Redis Stream
某个 Topic:
text
trade.executed
跟单服务订阅:
text
trade.executed
收到后判断:
text
userId=10001
是否带单员?
是
↓
查找所有跟单用户
↓
生成跟单订单
为什么不用撮合引擎直接 TCP 推给跟单服务?
如果直接:
text
撮合引擎
↓ TCP
跟单服务
会有几个问题:
耦合过高
跟单服务挂了:
text
撮合引擎怎么办?
不能因为跟单系统故障影响核心交易。
扩展困难
未来可能还有:
- 风控系统
- K线系统
- 清算系统
- 资产系统
- 推送系统
都需要成交数据。
如果全部 TCP:
text
撮合引擎
├─ TCP -> 风控
├─ TCP -> 跟单
├─ TCP -> K线
├─ TCP -> 清算
连接数量爆炸。
重放困难
跟单服务重启后:
text
刚才成交记录丢了
MQ 可以:
text
offset 回退
重新消费。
TCP 做不到天然重放。
头部交易所(币安、OKX、Bybit 等)一般是:
text
Matching Engine
↓
Internal Event Bus
↓
Copy Trading Service
这里甚至不一定是 Kafka。
可能是:
- Aeron
- Chronicle Queue
- Disruptor RingBuffer
- 自研 EventBus
因为 Kafka 延迟通常:
text
1~10ms
而跟单交易希望:
text
< 1ms
所以很多交易所采用:
text
撮合引擎
↓
内部高速事件总线
↓
跟单服务
然后再异步写 Kafka:
text
事件总线
├─ 跟单
├─ 风控
└─ Kafka
用户客户端收到成交通知又是什么方式?
跟单员或普通用户看到:
text
订单已成交
通常不是 MQ,而是:
text
成交事件
↓
Push Service
↓
WebSocket
↓
APP/Web
即:
text
撮合引擎
↓
MQ/EventBus
↓
Push Service
↓
WebSocket
↓
用户
现代交易所的主流设计是:
| 场景 | 通信方式 |
|---|---|
| 下单 → 撮合引擎 | TCP / FIX / 私有二进制协议 |
| 撮合引擎 → 成交回报 | 内部 TCP / 共享内存 / EventBus |
| 成交事件 → 跟单系统 | MQ(Kafka/Pulsar/RocketMQ)或高速 EventBus |
| 跟单系统 → 再次下单 | TCP |
| 成交通知用户 | WebSocket |
因此,对于"带单员成交后如何通知对应跟单员"的问题,在生产级系统中最常见的答案是:
成交事件先进入消息总线(Kafka、Pulsar、自研 EventBus 等),跟单服务订阅成交事件,再生成跟单订单;而不是撮合引擎直接通过 TCP 一对一通知跟单服务。