4. 低延迟网络技术:RDMA、DPDK、内核旁路与用户态协议栈

做高频交易的人,最怕什么?怕网络抖动,怕延迟不稳定。我见过太多团队,策略逻辑写得天衣无缝,结果被网络栈拖了后腿。说白了,传统Linux网络协议栈是为通用场景设计的,它要兼顾公平性、兼容性、安全性。但在高频交易里,我们只关心一件事——快。

这一章,我们来聊聊怎么把网络延迟从几十微秒压到几微秒,甚至亚微秒级别。核心思路就四个字:内核旁路

核心观点: 高频交易网络优化的本质,就是绕过操作系统内核,让应用程序直接与网卡硬件对话。

4.1 传统网络协议栈的瓶颈在哪?

先看一个典型的数据包旅程:网卡收到数据 → 硬件中断 → 内核中断处理 → 协议栈解析(IP/TCP) → 数据拷贝到用户态 → 系统调用返回。这一套下来,少说几十微秒。

我曾在某家交易公司做过一次 profiling,发现一个UDP包从网卡到用户态应用,平均耗时 45 微秒。其中真正花在协议处理上的时间不到 30%,剩下全是上下文切换、数据拷贝、中断处理的开销。

传统栈的三大痛点:

  • 中断风暴:高吞吐下,CPU 被中断淹没,连策略计算的时间都没了。
  • 数据拷贝:内核态到用户态至少一次拷贝,DMA 到内核缓冲区再到应用缓冲区,延迟翻倍。
  • 上下文切换:每次系统调用都要切一次,TLB 刷一遍,缓存全凉。

我的经验: 有一次排查延迟毛刺,发现罪魁祸首是网卡中断亲和性没配好,中断全打在一个 CPU 上。调整之后,P99 延迟直接降了 40%。别小看这些细节。

4.2 DPDK:让用户态接管网卡

DPDK(Data Plane Development Kit)是目前最主流的用户态网络方案。它的思路很简单:把网卡的控制权从内核手里夺过来,交给用户态程序

具体怎么做?

  • UIO 驱动:通过内核的 UIO(Userspace I/O)机制,把网卡寄存器映射到用户空间。
  • 轮询模式(Poll Mode Driver):放弃中断,CPU 死循环轮询网卡接收队列。没有中断开销,但 CPU 占用率 100%。
  • 大页内存(Hugepages):用 2MB 甚至 1GB 的大页,减少 TLB miss。
  • 无锁队列:网卡和应用程序之间通过 ring buffer 交换数据,避免锁竞争。

我刚开始用 DPDK 时犯过一个低级错误:没配大页内存,结果性能还不如内核。后来才发现,TLB miss 率高达 30%,配了 1GB 大页后直接降到 1% 以下。

# 分配 1024 个 2MB 大页
echo 1024 > /sys/kernel/mm/hugepages/hugepages-2048kB/nr_hugepages
# 挂载大页文件系统
mount -t hugetlbfs nodev /mnt/huge

DPDK 的典型延迟表现:在 10Gbps 网络上,UDP 包从网卡到应用,延迟可以做到 1-2 微秒。相比内核栈的 40+ 微秒,提升了一个数量级。

4.3 RDMA:绕过 CPU 直接访问内存

DPDK 虽然快,但数据还是要经过 CPU 拷贝到应用内存。RDMA(Remote Direct Memory Access)更进一步:网卡直接把数据写入应用的内存缓冲区,CPU 全程不参与

RDMA 有两种主流实现:

  • InfiniBand:专用网络,硬件和协议都是为 RDMA 设计的,延迟最低(亚微秒级)。
  • RoCEv2:基于以太网的 RDMA,兼容现有网络设备,但需要 PFC(优先级流控制)来保证无损。

我记得有个项目,两套交易系统之间需要同步订单簿数据。用 TCP 走,延迟 50 微秒;换成 InfiniBand RDMA,直接降到 1.2 微秒。当时团队都惊呆了。

注意: RDMA 的编程模型和传统 socket 完全不同。你需要管理内存注册、队列对(QP)、完成队列(CQ)。学习曲线陡峭,但回报巨大。

// RDMA 发送端核心代码片段(伪代码)
struct ibv_pd *pd = ibv_alloc_pd(context);
struct ibv_mr *mr = ibv_reg_mr(pd, buffer, size, IBV_ACCESS_LOCAL_WRITE);
struct ibv_qp *qp = create_qp(pd, cq);
// 发送数据
ibv_post_send(qp, &wr, &bad_wr);

4.4 用户态协议栈:自己写 TCP

DPDK 和 RDMA 主要针对 UDP 或自定义协议。但很多交易系统还是依赖 TCP——比如连接交易所的 FIX 协议。TCP 在内核里实现,延迟高不说,还不可控。

怎么办?自己实现一个用户态 TCP 协议栈

用户态协议栈的核心思路:

  • 基于 DPDK 收发包,在用户态完成 TCP 的拥塞控制、重传、滑动窗口。
  • 完全绕过内核 TCP 栈,避免系统调用和上下文切换。
  • 可以针对特定场景做优化,比如去掉慢启动、关闭 Nagle 算法。

市面上有现成的方案,比如 mTCP、F-Stack、Seastar。但我个人建议,如果团队有实力,最好自己写一个精简版。为什么?因为通用方案要考虑各种边界情况,而你的场景可能只需要处理一种流量模式。

我曾经帮一家做市商优化过他们的 FIX 网关。他们用内核 TCP,延迟 80 微秒。我们基于 DPDK 写了一个用户态 TCP 栈,只保留了必要的功能,延迟降到 8 微秒。代价是代码量从 2000 行涨到 8000 行,但值了。

避坑指南: 用户态协议栈最头疼的问题是 TCP 状态机。我曾经因为 TIME_WAIT 状态处理不当,导致连接泄漏,排查了两天。建议先把状态图画清楚再动手。

4.5 三种技术的选型对比

技术 延迟(典型值) CPU 开销 编程复杂度 适用场景
传统内核栈 40-100 μs 高(中断+拷贝) 非实时、管理面
DPDK 1-5 μs 中(轮询占 CPU) UDP 组播、行情解析
RDMA 0.5-2 μs 极低(零拷贝) 节点间同步、订单路由
用户态 TCP 5-15 μs FIX 网关、交易所连接

4.6 知识体系总览

下面这张图,是我自己总结的低延迟网络技术全景。你可以把它当作一个决策树:先看你的协议是 TCP 还是 UDP,再看延迟要求有多高,最后选对应的技术。

低延迟网络技术 内核旁路 零拷贝 用户态协议 DPDK(轮询模式) RDMA(InfiniBand/RoCE) mTCP / F-Stack UIO + 大页内存 无锁队列 内存注册(MR) QP / CQ 管理 TCP 状态机 拥塞控制精简 目标:将网络延迟从 40μs 降至 1-5μs 核心手段:绕过内核、零拷贝、用户态控制

嗯,这张图基本把本章的核心逻辑串起来了。你从根节点往下看,其实就是一条决策路径:要快,就得绕开内核。至于选 DPDK、RDMA 还是用户态协议栈,取决于你的具体场景和团队能力。

我个人建议,刚开始接触低延迟网络的同学,先从 DPDK 入手。它门槛相对低,社区活跃,踩坑也有人救。等把 DPDK 玩透了,再考虑 RDMA 或者自研协议栈。别一上来就想搞大而全,容易翻车。

一句话总结: 低延迟网络没有银弹。DPDK 适合行情解析,RDMA 适合节点间同步,用户态 TCP 适合 FIX 网关。选对技术,延迟减半;选错技术,加班加倍。


专注资料整理