4、网络协议与延迟优化:TCP vs UDP在高频场景下的选择,内核旁路技术(DPDK、Solarflare OpenOnload),低延迟网卡配置实战

做高频交易的人,最怕什么?

怕慢。

你想想看,别人从上海到深圳的行情,比你快1微秒,人家就能在你前面抢到单。这1微秒,可能就是几百万的利润差距。所以网络延迟,是我们这行吃饭的本钱。

今天我就把网络协议这块的坑和经验,一次性说清楚。

4.1 TCP vs UDP:高频场景下的生死抉择

先说结论:高频交易里,UDP是主流,TCP是备胎。

为什么?我拿一个真实场景给你讲。

我在2018年做港股行情系统时,一开始用的TCP。结果呢?行情一波动,TCP的拥塞控制就开始“温柔”了——丢包重传、滑动窗口、慢启动……这些机制在普通场景下是好事,但在高频场景下,就是灾难。

核心差异对比:

特性 TCP UDP
连接建立 三次握手(1.5 RTT) 无连接(0 RTT)
可靠性 自动重传 应用层自己处理
延迟抖动 高(拥塞控制导致) 低(无控制)
头部开销 20-60字节 8字节
适用场景 订单、风控 行情、心跳

说白了,TCP就像个老好人,什么事都帮你兜着。但高频交易不需要老好人,需要的是快枪手。

UDP呢?它只管发,不管收没收到。丢包了?你自己想办法。这反而给了我们最大的控制权。

我的经验:行情数据用UDP,订单用TCP。为什么?行情丢了可以等下一条,订单丢了就亏钱了。我见过有人把订单也放UDP上,结果丢了一笔单,亏了200万。嗯,血的教训。

4.2 内核旁路技术:绕过操作系统这个“中间商”

你可能会问:UDP已经很快了,为什么还要搞内核旁路?

因为操作系统本身就是个瓶颈。

数据从网卡到应用程序,要经过:网卡→内核协议栈→socket缓冲区→用户态。每一步都有上下文切换、内存拷贝。我测过,光这一步就能吃掉5-10微秒。

5微秒?在高频交易里,这已经是天文数字了。

4.2.1 DPDK:数据面开发套件

DPDK的思路很简单:让应用程序直接接管网卡。

它把网卡的数据直接映射到用户态内存,绕过内核。你想想看,少了内核这个“中间商”,速度能不快吗?

// DPDK初始化示例(简化版)
int main(int argc, char *argv[]) {
    // 1. 初始化DPDK环境
    rte_eal_init(argc, argv);
    
    // 2. 分配内存池
    struct rte_mempool *mbuf_pool = rte_pktmbuf_pool_create(
        "MBUF_POOL", NUM_MBUFS, MBUF_CACHE_SIZE, 0,
        RTE_MBUF_DEFAULT_BUF_SIZE, rte_socket_id());
    
    // 3. 初始化网卡
    struct rte_eth_conf port_conf = {
        .rxmode = { .max_rx_pkt_len = ETHER_MAX_LEN }
    };
    rte_eth_dev_configure(0, 1, 1, &port_conf);
    
    // 4. 启动网卡
    rte_eth_dev_start(0);
    
    // 5. 轮询收包(无中断)
    while (1) {
        struct rte_mbuf *pkts[BURST_SIZE];
        uint16_t nb_rx = rte_eth_rx_burst(0, 0, pkts, BURST_SIZE);
        // 处理数据包...
    }
}

这段代码的核心就一个词:轮询。传统方式是中断驱动,来一个包中断一次。DPDK是CPU死循环轮询,虽然占CPU,但延迟从微秒级降到了纳秒级。

注意:DPDK需要独占CPU核心。我建议至少留2个核心给DPDK,一个收包,一个处理。别贪心,否则你的系统会卡死。

4.2.2 Solarflare OpenOnload

DPDK好是好,但有个问题:你得重写你的网络代码。

OpenOnload就不一样了。它是个“透明加速”方案。你不需要改代码,只需要装个驱动,它自动把你的socket调用加速到硬件层面。

我记得第一次用OpenOnload时,心里还犯嘀咕:真有这么神奇?结果一测,延迟从10微秒降到了2微秒。嗯,真香。

两者对比:

特性 DPDK OpenOnload
代码改动 需要重写 无需改动
延迟 1-2微秒 2-3微秒
CPU占用 高(轮询)
学习成本

我的建议是:新系统用DPDK,老系统改造用OpenOnload。

4.3 低延迟网卡配置实战

光有软件优化还不够,硬件配置也得跟上。我踩过不少坑,给你列几个关键点。

4.3.1 网卡选型

别用普通网卡。高频交易圈子里,主流就两家:

  • Solarflare SFN系列:自带OpenOnload,延迟低到离谱
  • Mellanox ConnectX系列:支持RoCE,适合做RDMA

我个人偏好Solarflare,因为它的硬件时间戳精度高,对行情同步很有帮助。

4.3.2 BIOS和内核参数调优

这部分容易被忽略,但影响很大。

# 关闭CPU节能(防止频率波动)
echo 'performance' | tee /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor

# 关闭中断合并(减少延迟)
ethtool -C eth0 rx-usecs 0 tx-usecs 0

# 调整网卡ring buffer(防止丢包)
ethtool -G eth0 rx 4096 tx 4096

# 设置CPU亲和性(中断绑定到特定核心)
echo 2 > /proc/irq/$(cat /proc/interrupts | grep eth0 | awk '{print $1}' | sed 's/://')/smp_affinity

避坑指南:我曾经把rx-usecs设成0,结果CPU飙升到100%。后来发现是中断太多。解决办法是:把中断绑定到独立核心,别和业务核心混在一起。

4.3.3 硬件时间戳

高频交易里,时间就是金钱。但你知道网卡的时间戳有多重要吗?

没有硬件时间戳,你根本不知道行情数据是什么时候到的。软件时间戳有几十微秒的误差,这在高频场景下完全不可接受。

# 开启硬件时间戳
ethtool -T eth0

# 输出示例:
# Hardware Timestamping Capabilities:
#   rx-timestamping: yes
#   tx-timestamping: yes
#   hardware-transmit: yes
#   hardware-receive: yes

# 在代码中获取硬件时间戳
struct timespec ts;
ioctl(sockfd, SIOCGHWTSTAMP, &ts);

嗯,这里要注意:不是所有网卡都支持硬件时间戳。买之前一定要确认。

4.4 知识体系总览

说了这么多,我画张图帮你理清思路。

网络协议与延迟优化知识体系 协议选择 TCP:订单、风控(可靠但慢) UDP:行情、心跳(快但不可靠) 内核旁路技术 DPDK:用户态驱动,轮询模式 需重写代码,延迟1-2μs OpenOnload:透明加速 无需改代码,延迟2-3μs 低延迟网卡配置 网卡选型 Solarflare/Mellanox 内核参数调优 关闭节能/中断合并 硬件时间戳 纳秒级精度 核心目标:将端到端延迟控制在5微秒以内

这张图把今天的内容串起来了。从协议选择,到内核旁路,再到硬件配置,每一步都在跟延迟较劲。

最后说一句:网络优化没有银弹。TCP和UDP各有各的用处,DPDK和OpenOnload也各有优劣。关键是你得清楚自己的场景——是行情系统还是交易系统?是新建还是改造?想清楚了,再动手。

我曾经见过一个团队,花三个月把交易系统从TCP改成UDP,结果丢包率高了10倍,最后又改回去了。嗯,方向比努力更重要。


公众号:蓝海资料掘金营,微信deep3321