4、网卡选型:Solarflare vs Mellanox vs Intel、硬件时间戳、内核旁路(DPDK/OpenOnload)、多队列与RSS

网卡选型这事儿,我做了快十年了。说实话,很多团队一开始都低估了网卡的重要性。他们觉得CPU够快、内存够大就行,网卡随便配一块Intel的千兆卡就完事了。结果呢?行情数据一来,延迟直接崩了。

今天咱们就好好聊聊,低延迟行情系统里,网卡到底该怎么选。我会把Solarflare、Mellanox、Intel这三家的特点掰开揉碎了讲,顺便说说硬件时间戳、内核旁路、多队列这些关键技术。

4.1 三大网卡厂商的恩怨情仇

先说说这三家的定位。Intel是通用型选手,啥都能干,但啥都不精。Solarflare是专为金融交易设计的,低延迟是它的命根子。Mellanox(现在被NVIDIA收了)走的是高性能计算路线,带宽大、延迟低,但生态偏HPC。

我个人的习惯是:做极低延迟交易系统,首选Solarflare。为什么?因为它从硬件到软件,全链路都为低延迟优化过。

特性 Solarflare Mellanox Intel
硬件时间戳精度 纳秒级(<10ns) 纳秒级(~20ns) 微秒级(~1μs)
内核旁路方案 OpenOnload(自带) MLX5驱动 + DPDK DPDK(需额外配置)
多队列支持 128队列 64队列 32队列
典型延迟(DPDK模式) ~1μs ~1.5μs ~3μs
价格 中高

你看这个表格,差距很明显。Intel的硬件时间戳精度只有微秒级,对于高频交易来说,这基本等于没有。Solarflare能做到10纳秒以内,这才是真正能用的时间戳。

核心观点:如果你的系统要求端到端延迟在5微秒以内,别犹豫,直接上Solarflare。如果预算有限,Mellanox也是个不错的选择。Intel嘛...做做后台数据同步还行,做行情接收就算了。

4.2 硬件时间戳:没有它,延迟分析就是扯淡

硬件时间戳是什么?说白了,就是网卡在收到数据包的瞬间,用硬件打上一个时间标记。这个标记不经过CPU、不经过操作系统,精度极高。

我曾经在一个项目中遇到过这样的坑:团队用软件时间戳来测量延迟,结果发现数据波动特别大。后来一查,原来是CPU在打时间戳的时候被中断打断了,时间戳直接偏了几十微秒。你想想看,几十微秒的误差,对于微秒级的交易系统来说,这数据还有什么意义?

硬件时间戳的工作原理是这样的:

// 伪代码:硬件时间戳的获取流程
1. 网卡收到数据包
2. 硬件立即读取PTP时钟(或NIC内部时钟)
3. 将时间戳嵌入到数据包的元数据中
4. 应用程序通过DPDK/OpenOnload直接读取这个时间戳
5. 整个过程不经过内核,延迟固定

嗯,这里要注意:不是所有网卡都支持硬件时间戳。Intel的某些低端型号就不支持,或者只支持软件模拟的。买之前一定要看规格书,确认是"Hardware Timestamping"而不是"Software Timestamping"。

实战技巧:在Solarflare网卡上,可以用 sfcaffinity 工具查看硬件时间戳的精度。我一般会跑一个 sfptp -i eth0 -m 命令,看看PTP同步的抖动有多大。如果抖动超过50纳秒,那说明时钟同步有问题,得排查。

4.3 内核旁路:DPDK vs OpenOnload

内核旁路,说白了就是让应用程序直接跟网卡对话,跳过操作系统内核。为什么要跳过?因为内核太慢了。一次系统调用、一次中断处理、一次内存拷贝,几十微秒就没了。

目前主流的方案有两个:DPDK和OpenOnload。

4.3.1 DPDK:开源、灵活、但折腾

DPDK是Intel主导的开源项目,现在生态很成熟。它的核心思想是:用户态轮询 + 大页内存 + 无锁队列

我个人的经验是:DPDK适合有专门团队维护的场景。因为你要自己写驱动、自己管理内存池、自己处理中断。好处是灵活,坏处是坑多。

// DPDK初始化示例(简化版)
rte_eal_init(argc, argv);          // 初始化EAL
rte_pktmbuf_pool_create("mbuf_pool", NB_MBUF, 0, 0, RTE_MBUF_DEFAULT_BUF_SIZE, rte_socket_id());
rte_eth_dev_configure(port, nb_rx_queue, nb_tx_queue, &port_conf);
rte_eth_rx_queue_setup(port, 0, NB_RX_DESC, rte_eth_dev_socket_id(port), &rx_conf, mbuf_pool);

你看,光初始化就这么多步骤。我曾经帮一个团队排查DPDK性能问题,发现他们忘了设置 rte_eth_dev_set_mtu,导致巨帧没生效,延迟直接翻倍。这种坑,没踩过的人根本想不到。

4.3.2 OpenOnload:Solarflare的亲儿子

OpenOnload是Solarflare自家的内核旁路方案。它的最大优势是:零代码修改。你只需要把应用程序链接到OpenOnload的库,它自动就把网络栈从内核切到用户态了。

说白了,OpenOnload就是一个LD_PRELOAD的库。你运行程序的时候加上 LD_PRELOAD=libonload.so,它就接管了所有的socket操作。对于已有的代码,几乎不需要改动。

对比总结:

  • DPDK:需要重写网络代码,性能上限高,但开发成本大
  • OpenOnload:零代码修改,部署简单,但只能用在Solarflare网卡上
  • 我的建议:新项目用DPDK,老项目迁移用OpenOnload

4.4 多队列与RSS:别让一个CPU核累死

多队列(Multi-Queue)和RSS(Receive Side Scaling)是干嘛的?简单说,就是让多个CPU核同时处理网络数据包,而不是让一个核忙死、其他核闲死。

你想想看,如果只有一个队列,所有数据包都往一个CPU核上送。行情数据一来,那个核的CPU使用率直接飙到100%,其他核却在旁边看热闹。这不是浪费吗?

RSS就是解决这个问题的。它通过哈希算法(比如根据IP、端口号),把数据包均匀地分配到不同的队列上。每个队列绑定一个CPU核,这样就能并行处理了。

注意:RSS的哈希算法不是万能的。如果哈希冲突严重,某些队列还是会过载。我遇到过一种情况:某个行情源的所有数据包都来自同一个IP和端口,RSS哈希结果全落在一个队列上。解决办法是手动调整哈希key,或者用Flow Director做精确分流。

Solarflare网卡在这方面做得最好。它支持128个硬件队列,而且每个队列可以独立配置中断亲和性。Mellanox支持64个,Intel只有32个。对于高并发行情系统,队列数量越多,分流越精细。

配置多队列的典型做法:

# 设置网卡队列数(以Solarflare为例)
ethtool -L eth0 combined 8

# 查看当前队列分布
ethtool -l eth0

# 设置中断亲和性(绑定到特定CPU核)
echo 1 > /proc/irq/$(cat /sys/class/net/eth0/device/msi_irqs | head -1)/smp_affinity

嗯,这里有个小技巧:队列数最好等于物理CPU核数,而不是超线程核数。因为超线程核共享L1/L2缓存,如果两个队列绑到同一个物理核的两个超线程上,反而会互相干扰。

4.5 一张图看懂网卡选型逻辑

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

网卡选型决策流程图 开始选型 延迟要求 < 5μs? Solarflare Intel 预算充足? SFN8522 SFN7x42 用DPDK? X710-DA4 X550-T2 最终建议:Solarflare + OpenOnload 或 Mellanox + DPDK(预算有限时)

这张图的核心逻辑很简单:先看延迟要求,再看预算,最后看技术栈。别一上来就纠结DPDK还是OpenOnload,先搞清楚你的业务到底需要多低的延迟。

4.6 我的最终建议

做了这么多年低延迟系统,我总结了几条铁律:

  • 硬件时间戳是刚需,没有它,你的延迟数据就是假的。别省这个钱。
  • 内核旁路是必选项,不管是DPDK还是OpenOnload,必须用。用内核原生协议栈做行情接收,那是自寻死路。
  • 多队列要配好,队列数=物理核数,中断亲和性要绑对。我曾经见过有人把两个队列绑到同一个核上,结果性能还不如单队列。
  • Solarflare是首选,Mellanox是备选,Intel是最后的选择。别为了省几千块钱,让整个系统的延迟多出几微秒。

嗯,最后说一句:网卡选型这事儿,没有银弹。你得根据自己的业务场景、预算、团队技术能力来综合判断。但有一条原则永远不会变——在低延迟系统里,网卡就是你的命门。选对了,事半功倍;选错了,后面再怎么优化都白搭。