网络协议与延迟:TCP/IP优化、UDP应用、RDMA技术、时间同步

做市商系统里,网络延迟就是真金白银。我见过太多团队,策略模型跑得漂亮,结果在网络传输上多花了几个微秒,订单就被人抢了。说白了,网络这块是量化系统的「血管」,堵了哪都不行。

今天咱们聊聊四个核心话题:TCP/IP怎么优化、UDP在什么场景下好用、RDMA到底能快多少、以及时间同步为什么是刚需。嗯,这些都是我踩过坑之后才真正理解的东西。

TCP/IP优化:别让协议栈拖后腿

很多人觉得TCP是「可靠」的代名词,但在高频场景下,它的拥塞控制、重传机制反而成了累赘。我个人习惯,做市商系统内部通讯尽量避开TCP,但如果非用不可,有几个优化点必须做。

核心优化方向:

  • 禁用Nagle算法:TCP_NODELAY必须打开。Nagle会把小包攒成大包再发,攒包的时间在交易系统里就是灾难。
  • 调整缓冲区大小:默认的收发缓冲区太小,容易导致丢包重传。我建议把SO_RCVBUF和SO_SNDBUF调到至少1MB以上。
  • 使用epoll而非select/poll:select有1024个文件描述符限制,poll虽然没限制但性能差。epoll是Linux下IO多路复用的最优解。
  • 开启快速重传:调整tcp_retries1和tcp_retries2,让内核更快感知丢包。

我曾经在一个项目里,发现两台机器之间TCP传输偶尔会卡顿几十毫秒。查了半天,原来是Nagle算法和延迟ACK机制在打架——一个攒包,一个等包,互相等成了死锁。嗯,从那以后我所有交易系统的TCP连接都强制关掉Nagle。

// 禁用Nagle算法示例
int flag = 1;
setsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY, (char *)&flag, sizeof(flag));

// 调整收发缓冲区
int rcvbuf = 1024 * 1024;  // 1MB
setsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, &rcvbuf, sizeof(rcvbuf));
int sndbuf = 1024 * 1024;
setsockopt(sockfd, SOL_SOCKET, SO_SNDBUF, &sndbuf, sizeof(sndbuf));

UDP应用:牺牲可靠性换速度

UDP没有握手、没有重传、没有拥塞控制。说白了,它就是「发了就不管」。这在做市商系统里反而成了优点——行情数据丢了就丢了,下一笔马上就到,没必要重传。

但UDP有个大坑:丢包。你想想看,如果行情网关用UDP广播,某个瞬间网络拥堵,你的系统可能漏掉一笔关键报价。怎么解决?

  • 应用层序列号:每个UDP包带一个递增的seq_id,接收方检测到跳号就知道丢了包。
  • 冗余编码:用FEC(前向纠错)技术,发送方多带一些冗余数据,接收方即使丢了一部分也能恢复。
  • 多通道备份:同时从两个不同的网络路径接收同一份数据,哪个先到用哪个。

我的经验:UDP接收缓冲区一定要调大。Linux默认的UDP接收缓冲区只有几百KB,行情数据稍微一爆发就丢包。我一般设到16MB以上。

// 调整UDP接收缓冲区
int rcvbuf = 16 * 1024 * 1024;  // 16MB
setsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, &rcvbuf, sizeof(rcvbuf));

RDMA技术:绕过内核的极致加速

RDMA(Remote Direct Memory Access)是真正的「黑科技」。它允许一台机器的应用直接读写另一台机器的内存,完全绕过操作系统内核和CPU。延迟能低到什么程度?微秒级。

我记得第一次在实验室测RDMA,两台机器之间ping延迟是0.3微秒,而TCP要10微秒以上。当时我就觉得,这才是做市商系统该用的东西。

RDMA的三种实现方式:

方式 特点 适用场景
InfiniBand 专用硬件,延迟最低 核心交易引擎互联
RoCE 基于以太网,成本较低 行情分发、订单路由
iWARP 基于TCP,兼容性好 跨数据中心通信

但RDMA也有坑。它需要专门的网卡和交换机,配置起来相当复杂。我曾经在部署RoCE时,因为PFC(优先级流控)参数没调好,导致整个网络出现死锁,所有交易都停了。嗯,从那以后我学乖了——RDMA的配置一定要做充分的压力测试。

// RDMA编程示例(简化版)
// 使用libibverbs库

struct ibv_context *ctx = ibv_open_device(ibv_get_device_list(NULL)[0]);
struct ibv_pd *pd = ibv_alloc_pd(ctx);
struct ibv_mr *mr = ibv_reg_mr(pd, buffer, size, IBV_ACCESS_LOCAL_WRITE);

// 创建QP(队列对)
struct ibv_qp_init_attr attr = {0};
attr.qp_type = IBV_QPT_RC;  // 可靠连接
struct ibv_qp *qp = ibv_create_qp(pd, &attr);

// 发起RDMA写操作
ibv_wr_start(qp);
ibv_wr_rdma_write(qp, remote_addr, remote_rkey, local_addr, length);
ibv_wr_complete(qp);

时间同步:没有精确时钟,一切白搭

做市商系统里,时间戳就是证据。交易所问你「这笔订单是什么时候发的」,你拿不出精确到微秒的时间戳,那就有理说不清。

NTP(网络时间协议)精度在毫秒级,对于高频交易来说完全不够。PTP(精确时间协议,IEEE 1588)才是正解,它能做到亚微秒级的同步精度。

注意:PTP需要硬件支持。如果你的网卡不支持硬件时间戳,那PTP的精度会大打折扣。我建议直接买支持PTP的网卡和交换机,别在软件层面折腾。

我曾经在一个项目里,两台服务器用NTP同步,结果时间差了50毫秒。导致同一个订单在两台机器上记录的时间戳前后颠倒,排查问题花了两天。后来换成PTP,问题再没出现过。

时间同步的部署要点:

  • 主时钟源:用GPS或北斗接收机作为一级时钟,精度最高。
  • 边界时钟:交换机开启PTP边界时钟模式,减少累积误差。
  • 透明时钟:如果交换机不支持边界时钟,至少用透明时钟模式,修正驻留时间。
  • 硬件时间戳:确保所有网卡和应用都使用硬件时间戳,而不是软件时间戳。
// 使用PTP获取硬件时间戳示例(Linux)
#include <linux/ptp_clock.h>

int fd = open("/dev/ptp0", O_RDWR);
struct ptp_clock_time ptp_time;
ioctl(fd, PTP_CLOCK_GETTIME, &ptp_time);

// ptp_time.sec 和 ptp_time.nsec 就是精确到纳秒的时间
printf("PTP time: %ld.%09ld\n", ptp_time.sec, ptp_time.nsec);

知识体系总览

下面这张图把本章的核心逻辑串起来了。你可以看到,从TCP/IP到UDP再到RDMA,延迟越来越低,但复杂度也越来越高。时间同步则是贯穿所有网络技术的「基准线」。

网络协议与延迟优化知识体系 TCP/IP优化 UDP应用 RDMA技术 时间同步 延迟递减方向 → 延迟:1-10ms 适用:管理/监控 延迟:10-100μs 适用:行情广播 延迟:1-10μs 适用:订单路由 精度:亚微秒 适用:全系统 核心原则 1. 延迟敏感度决定协议选择:越高频越要靠近RDMA 2. 时间同步是基础设施:没有精确时钟,延迟优化失去意义 3. 硬件加速是终极方案:从网卡到交换机,每一环都不能省

好了,网络协议这块就聊到这儿。TCP/IP优化是基本功,UDP适合行情广播,RDMA是终极武器,时间同步则是所有优化的前提。你想想看,如果连时间都对不准,延迟再低又有什么用?

一句话总结:做市商系统的网络设计,就是在延迟、可靠性和成本之间找平衡。没有银弹,只有最适合你场景的方案。

交易系统化学习资料 微信Strategy888888