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和OpenOnload也各有优劣。关键是你得清楚自己的场景——是行情系统还是交易系统?是新建还是改造?想清楚了,再动手。
我曾经见过一个团队,花三个月把交易系统从TCP改成UDP,结果丢包率高了10倍,最后又改回去了。嗯,方向比努力更重要。
公众号:蓝海资料掘金营,微信deep3321