4. 行情推送链路中的时间节点
在行情推送系统中,“时间”从来不是一个单一概念。同一条行情从产生到被策略使用,至少会经过四个性质完全不同的时间节点:交易所撮合时间、行情网关发送时间、客户端接收时间、本地处理时间。很多时间戳校验失效、延迟统计失真、跨时区换算错误的根因,并不是时钟本身不准,而是把这四个时间混为一谈,或者记录时没有标明它属于哪一段链路。
理解这四个节点的关键,是先建立一条清晰的链路参照。以一条股票逐笔成交行情为例:交易所撮合引擎在完成撮合后,为该笔成交打上撮合时间;行情网关从交易所或上游行情源取到这条消息,在向外转发时打上网关发送时间;客户端网卡收到数据包,应用层解析出消息,记录客户端接收时间;随后策略代码对消息做解析、字段映射、指标计算、入库等处理,处理完成时记录本地处理时间。四个时间点依次递增,但它们分别度量的是“市场事件发生”“上游对外发布”“本机收到”“本机处理完”四个不同阶段。
这四个时间节点在工程上最容易出问题的地方,是它们分属不同的时钟域。交易所撮合时间通常以交易所本地时间或 UTC 表示,网关发送时间由网关服务器时钟产生,客户端接收时间和本地处理时间则由客户端本机时钟产生。如果直接拿“客户端接收时间减去交易所撮合时间”当作端到端延迟,得到的数值里既包含网络传输,也包含两端时钟偏差,甚至包含时区换算错误。因此记录时必须做到两点:一是每个时间戳都带明确的时区标识或统一换算为 UTC 存储;二是每个时间戳都标注来源节点,不能只存一个裸时间字段。
具体到记录方式,建议在消息结构里为四个时间分别保留独立字段,而不是复用一个 time 字段。例如一条行情记录可以设计为:exchange_match_ts(交易所撮合时间,UTC 毫秒)、gateway_send_ts(网关发送时间,UTC 毫秒)、client_recv_ts(客户端接收时间,UTC 毫秒)、local_process_ts(本地处理时间,UTC 毫秒),并额外记录 client_tz_offset(客户端时区偏移)与 clock_source(时钟来源,如 NTP/PTP/本地系统)。这样在后续分析时,可以分别计算三段耗时:网关段 = gateway_send_ts − exchange_match_ts,网络段 = client_recv_ts − gateway_send_ts,处理段 = local_process_ts − client_recv_ts。三段相加才是完整的端到端耗时,任何一段异常都能被单独定位。
下面是一段示例性的记录代码,用于说明字段如何落库。示例参数为假设值,仅用于演示结构:
# 示例:行情消息时间戳记录结构(参数为假设示例)
record = {
"symbol": "600000",
"exchange_match_ts": 1717056000123, # UTC 毫秒,交易所撮合
"gateway_send_ts": 1717056000148, # UTC 毫秒,网关发送
"client_recv_ts": 1717056000176, # UTC 毫秒,客户端接收
"local_process_ts": 1717056000189, # UTC 毫秒,本地处理完成
"client_tz_offset": "+08:00",
"clock_source": "NTP"
}
# 分段耗时(毫秒)
gateway_seg = record["gateway_send_ts"] - record["exchange_match_ts"] # 25
network_seg = record["client_recv_ts"] - record["gateway_send_ts"] # 28
process_seg = record["local_process_ts"]- record["client_recv_ts"] # 13
e2e_seg = record["local_process_ts"]- record["exchange_match_ts"] # 66
需要特别强调的是,客户端接收时间与本地处理时间虽然都来自本机时钟,但含义不同。接收时间反映的是“消息到达本机”的时刻,处理时间反映的是“本机完成业务处理”的时刻,两者之差才是本地处理耗时。如果把处理时间误当成接收时间使用,就会把本地计算开销错误地算进网络延迟,导致排障方向完全跑偏。反过来,如果只记录接收时间而不记录处理时间,就无法判断策略延迟究竟来自网络还是来自本地计算。
在实际校验中,还可以利用这四个时间点的单调性做一致性检查:正常情况下应满足 exchange_match_ts ≤ gateway_send_ts ≤ client_recv_ts ≤ local_process_ts。若出现倒挂,通常意味着某一端时钟未同步、时区换算错误,或消息乱序。把这条不等式作为入库前的校验规则,可以在数据源头拦截大量时间戳异常,而不是等到策略回测或延迟统计时才发现问题。
总结来说,区分这四个时间节点的本质,是区分“市场事件发生”“上游发布”“本机收到”“本机处理完”四个阶段,并为每个阶段保留独立、带时区、带来源的时间字段。只有记录粒度足够细,后续的延迟归因、时钟校验和时区换算才有可靠基础。