三亩地 三亩地SAN MU DI · CODE DIARY
ARTICLE DETAIL

日记详情

真实记录编程学习的某一天,欢迎挑你感兴趣的翻一翻。

为什么 DPDK 系统上线后会随机卡顿?——一次生产级 Latency Spike 的深度排障实录

为什么 DPDK 系统上线后会随机卡顿?——一次生产级 Latency Spike 的深度排障实录

一、问题背景:实验室一切正常,上线后却随机丢包

项目背景:

一个基于 DPDK 的用户态 UPF 数据面。

硬件环境:

双路 Xeon 100G Mellanox NIC NUMA 双节点

系统架构:

RSS RX Queue ↓ Worker Pipeline ↓ Flow Lookup ↓ GTP-U Encap ↓ TX Queue

实验室压测结果:

稳定 80+ Gbps Latency < 20us 无丢包

看起来非常完美。

但上线后,问题出现了:每隔几十分钟系统会突然:

Latency Spike

现象:

  • 时延从 20us 飙升到 2ms

  • 持续几十毫秒

  • 短暂丢包

  • PPS 波动

  • CPU 利用率无明显异常

更致命的是:

无法稳定复现

这类问题才是真正折磨 DPDK 工程师的噩梦。


二、为什么这类问题极难定位?

因为传统性能问题:

持续存在

例如:

  • CPU 跑满

  • 锁竞争

  • 吞吐不足

这些问题:perf 一跑就知道。

但 Latency Spike本质是:

瞬时微观系统失稳

问题可能只持续:

几十微秒 ~ 几十毫秒

普通工具根本抓不到。


三、第一阶段排查:怀疑业务逻辑

最开始大家都认为:

是不是 Flow Table 太慢?

于是:

  • 优化 hash

  • 增加 prefetch

  • 减少 branch miss

  • cache line 对齐

结果:平均 PPS 更高了。

但:

Spike 依然存在

说明:问题不在业务层。


四、第二阶段:怀疑 NUMA

后来,我们开始怀疑:

NUMA Remote Access

因为网卡在:

NUMA0

部分 worker:

在:

NUMA1

于是:

我们做了:

  • CPU 绑核

  • Queue 绑核

  • HugePage NUMA 绑定

  • mempool 分 NUMA

结果:平均 latency 降低了。

但:

Spike 仍然存在

只是:频率降低了。

说明:NUMA 只是部分原因。


五、真正的问题开始浮现:Mempool Cache 抖动

后来,我们注意到一个奇怪现象:

Spike 出现时,某些 core:

rte_mempool_get_bulk() 耗时突然暴涨

正常:

几十 ns

Spike 时:

数十 us

这意味着:

mempool 分配异常

六、DPDK Mempool 真正的隐藏机制

很多人以为:

mempool 就是对象池

其实远没那么简单。

DPDK mempool:

有:

Per-Core Cache

结构:

Core Local Cache ↓ Global Ring

正常情况下:core 从本地 cache 获取 mbuf。速度极快。

但:当 local cache 耗尽时。

会:

批量从 Global Ring 获取

而 Global Ring是:

多核共享

这时候:问题开始了。


七、真正的隐性炸弹:Remote Free

后来我们终于发现:

系统采用:

RX Core 分配 mbuf TX Core 释放 mbuf

这意味着:

mbuf 分配和释放 不在同一个 core

于是:

大量 mbuf会跨核返回。

这会导致:

Per-Core Cache 被污染

进一步导致:

  • local cache miss

  • global ring 竞争

  • cache bouncing

最终:在某个时间点,大量 core:同时访问:

Global Mempool Ring

于是,系统瞬间进入:

cache coherency storm

这才是:Latency Spike 的真正源头。


八、为什么平均性能很好,但 P99 极差?

因为:

绝大部分时间,系统都运行在:

cache hot

状态。

但偶尔会触发:

cache collapse

这时:CPU 会:

  • LLC miss

  • remote access

  • memory ordering stall

  • atomic contention

于是:P99 latency 暴涨。


九、为什么 perf 很难抓到?

因为:Spike可能:

100ms 才出现一次

而 perf通常:

统计平均值

因此:你会看到:

平均 perf 很漂亮

但:

真实线上,已经开始丢包。


十、真正的杀手:Memory Ordering

后来我们继续深入。

发现:

即使没有锁,系统依然会:

stall

原因:很多 rte_ring 操作内部使用:

atomic CAS memory barrier

例如:

__atomic_compare_exchange_n()

这类指令会导致:

CPU pipeline flush

尤其多核下:代价巨大。


十一、为什么“无锁”不等于“高性能”?

很多人误解:

lock-free = 无开销

实际上:

无锁数据结构往往会:

大量消耗 cache coherency

特别是在:

高频 producer/consumer

场景。

因此:

真正高性能系统反而会:

减少共享

而不是:

疯狂 lock-free

十二、Burst 调度为什么也会引发 Spike?

后来。

我们还发现:Burst Size:并不是越大越好。

例如:

rte_eth_rx_burst(..., 32)

改成:

128

平均吞吐更高。

但:

P99 latency 更差。

为什么?

因为:Burst 越大:意味着:

包等待时间越长

例如:

必须攒够:

128 个包

才会进入下一阶段。

于是:小流量时会出现:

Head-of-Line Blocking

这也是:很多系统:

平均值很好 尾时延极差

的原因。


十三、另一个隐形问题:PCIe Backpressure

继续排查后。

我们还发现:

某些 spike 时刻:NIC TX Queue:

Descriptor 回收变慢

原因:

PCIe 总线会出现:

瞬时拥塞

尤其:

  • 多队列 DMA

  • NUMA 跨节点

  • IOMMU 开启

场景。

这会导致NIC 无法及时:

write-back descriptor

于是:TX recycle 延迟。

进一步触发:

mempool starvation

最后:

整个系统:进入短暂雪崩。


十四、IOMMU 为什么也会影响 DPDK?

很多人开启:

intel_iommu=on

是为了:VFIO 安全隔离。

但:

IOMMU本质也是:

地址转换

DMA 时也会:

查 IOTLB

如果:IOTLB miss:

就会:

触发 page walk

在高 PPS 下。

这会形成:

额外 latency spike

因此:很多极致性能系统会:

关闭 IOMMU

或者:

使用:

1GB HugePage

减少 IOTLB 压力。


十五、真正稳定的数据面系统怎么设计?

后来,我们彻底重构系统。

核心原则:


1. 包生命周期不跨核

原则:

谁分配 谁释放

避免:

Remote Free

2. One Queue One Core

避免:

共享队列

3. 避免全局共享结构

包括:

  • flow counter

  • stats

  • timer wheel

全部:

per-core 化

4. 小 Burst + 高频调度

不要盲目追求:

最大吞吐

而要:

平衡 tail latency

5. NUMA 严格隔离

包括:

  • NIC

  • Queue

  • HugePage

  • Worker

  • mempool

全部:

NUMA 对齐

十六、DPDK 真正难的,从来不是收包

很多新手觉得:DPDK 难点是:

rte_eth_rx_burst()

其实完全不是。

真正困难的是:

长期稳定低时延

因为:

一旦进入:

  • 100G

  • 千万 PPS

  • 多 NUMA

  • 多 Socket

场景。

系统瓶颈:

已经不是:

网络

而是:

CPU 微架构 内存系统 PCIe Cache coherency

十七、现代 DPDK 调优,本质已经是微架构工程

很多人以为:

网络开发优化的是:

协议

实际上:

真正高端的数据面优化。

最后都在优化:

  • cache line

  • prefetch

  • memory ordering

  • TLB

  • PCIe DMA

  • NUMA locality

本质:

已经进入:

CPU 微架构领域

十八、总结

很多 DPDK 系统:

实验室跑得很好。

上线后:

却会:

  • 随机抖动

  • latency spike

  • 瞬时丢包

  • P99 爆炸

根本原因:通常并不是:

业务逻辑太慢

而是:

系统进入了 cache/memory 失稳状态

真正的大规模 DPDK 系统。

最终比拼的不是:

谁 PPS 更高

而是:

谁能长期稳定维持低 tail latency

而这背后。

拼的已经不是网络知识。

而是:

CPU 微架构理解能力
← 返回列表