3. 数据采集层架构:多源异构数据接入

数据采集层,说白了就是交易监控系统的「耳朵」和「眼睛」。

你想想看,一个监控系统要是连数据都拿不到,或者拿到的数据是错的、延迟的,那后面的分析、告警全是白搭。我个人习惯把这一层叫做「数据入口的守门员」——它决定了整个系统的数据质量天花板。

3.1 三大数据源:交易所API、FIX协议、消息队列

在实际项目中,我们面对的从来不是单一数据源。我做过一个监控系统,同时接了3家交易所的REST API、2条FIX链路、还有内部Kafka集群的行情流。嗯,这里面的坑,我一个个说。

核心原则:数据采集层必须做到「统一接入、差异屏蔽、质量可控」。

3.1.1 交易所API接入

交易所API是最常见的数据来源。但每家交易所的接口风格、限流策略、数据格式都不一样。

  • REST API:适合低频数据(如订单状态、账户余额)。我建议用连接池+重试机制,避免单点故障。
  • WebSocket:适合高频行情(如逐笔成交、深度快照)。注意心跳保活和断线重连。
  • 限流处理:我曾经遇到一家交易所,每秒只允许10次请求。超了直接封IP。后来我加了令牌桶算法,才稳住。
// 伪代码:交易所API接入适配器
public class ExchangeAdapter {
    private RateLimiter limiter = RateLimiter.create(10); // 每秒10次
    
    public MarketData fetchDepth(String symbol) {
        limiter.acquire(); // 限流
        try {
            return httpClient.get("/depth", symbol);
        } catch (RateLimitExceeded e) {
            // 退避重试
            Thread.sleep(1000);
            return fetchDepth(symbol);
        }
    }
}

3.1.2 FIX协议接入

FIX协议在机构交易中几乎是标配。它是个二进制协议,解析起来比JSON麻烦得多。

我记得第一次接触FIX时,被它的Tag-Value格式搞晕了。比如 35=D 表示新订单,55=600519 表示股票代码。每个字段都要查字典。

我的经验:不要自己手写FIX解析器。用现成的库,比如QuickFIX/J或QuickFIX/C++。它们已经处理了会话层、序列号、重传等复杂逻辑。

FIX接入的几个关键点:

  • 会话管理:FIX是长连接,需要维护SeqNum。我曾经因为SeqNum错乱,导致整个链路重连了半小时。
  • 心跳机制:默认30秒一次心跳。如果连续3次没收到,就要触发重连。
  • 日志记录:所有FIX消息必须落盘。出问题时,这是唯一的排查依据。

3.1.3 消息队列接入

消息队列是内部数据流转的「高速公路」。我们常用Kafka或RabbitMQ。

为什么需要消息队列?说白了,就是解耦。采集层只管收数据,处理层只管消费数据。中间用MQ缓冲,谁都不拖累谁。

特性 Kafka RabbitMQ
吞吐量 百万级/秒 万级/秒
消息顺序 分区内有序 队列内有序
持久化 磁盘存储 内存+磁盘
适用场景 高吞吐、日志、行情 低延迟、任务分发
注意:消息队列不是万能的。如果监控系统对延迟要求极高(毫秒级),MQ的序列化/反序列化开销可能成为瓶颈。这时候可以考虑直接内存传输。

3.2 数据采集架构图

下面这张图是我自己项目里用过的架构。它把三大数据源统一接入,然后做标准化处理,最后写入Kafka供下游消费。

数据采集层架构图 交易所API FIX协议 消息队列 API适配器 FIX解析器 MQ消费者 数据标准化(统一Schema、时间戳对齐、去重) Kafka Topic(行情/订单/成交) 三大数据源 → 适配器 → 标准化 → 统一输出

3.3 数据质量与异常处理

数据采集层最容易出问题的,不是「拿不到数据」,而是「拿到错误的数据」。

我曾经遇到一个场景:交易所API返回的行情数据,某只股票的价格突然跳了100倍。后来发现是交易所的bug,但我们的监控系统直接触发了虚假告警。

解决方案:在采集层加入数据校验规则。
  • 价格范围校验(比如不能为负,不能超过前值的10倍)
  • 时间戳单调递增校验
  • 重复数据去重(基于消息ID或时间戳)

3.4 性能优化要点

数据采集层往往是整个系统的瓶颈。我总结了几条优化经验:

  1. 批量处理:不要一条一条地发。攒够100条或100ms,批量写入Kafka。吞吐量能提升10倍。
  2. 零拷贝:如果使用Netty或Kafka,开启零拷贝模式。减少数据在内核态和用户态之间的拷贝。
  3. 异步非阻塞:所有I/O操作都用异步。别用同步阻塞,否则一个慢接口拖死整个采集线程。
  4. 内存池:高频场景下,频繁new对象会导致GC抖动。用对象池复用ByteBuf或Message对象。
避坑指南:我曾经在采集层用了同步的HTTP客户端,结果一个交易所的API响应慢了5秒,导致整个采集线程池阻塞。后来换成异步的WebClient,问题解决。

3.5 总结

数据采集层,说白了就是「接进来、洗干净、送出去」。

接进来要兼容多源,洗干净要校验质量,送出去要高效可靠。这三个环节,任何一个出问题,监控系统就是「睁眼瞎」。

我个人建议,在设计阶段就把数据采集层的容错、限流、监控指标(比如采集延迟、丢失率)都考虑进去。别等到上线了再补,那时候就晚了。

专注资料整理