一、问题出现了某运营商边缘节点部署了一套用户面网关。系统采用DPDK 多核Worker模型主要功能GTP-U解封装 ↓ Session查找 ↓ PDR/FAR匹配 ↓ 转发整体架构如下N3 | | ------------- | Gateway | ------------- | | N6实验室压测结果项目数值Worker16CPU频率3.0GHzSession10万PPS350万时延80us结果非常理想。上线一个月后却收到告警流量增长后出现丢包 业务时延明显升高但查看系统状态时却发现一个奇怪现象top结果16个Worker 全部100%开发人员第一反应CPU已经满了需要增加服务器。但事实证明这完全是误判。二、DPDK为什么CPU永远100%先解释一个很多传统Linux开发者容易误解的问题。Linux Socket程序while(1) { recvfrom(fd,...); }没包时线程睡眠CPU利用率下降。而DPDKwhile(1) { nb_rx rte_eth_rx_burst(...); process(); rte_eth_tx_burst(...); }即使没有任何流量Worker仍在轮询RX Ring因此CPU永远100%这意味着CPU利用率 不能作为DPDK程序性能指标真正重要的是PPS Cycle Per Packet Cache Miss三、问题的第一现场进一步统计指标数值Worker数16CPU100%PPS80万Session220万奇怪的地方出现了实验室10万Session 350万PPS现网220万Session 80万PPSCPU同样100%。为什么性能差了4倍四、perf告诉了真相执行perf stat -e cycles,instructions,cache-misses结果cycles 3.2e12 instructions 2.1e12 cache-misses 5.8e10继续perf record perf report热点函数rte_hash_lookup占比42%这意味着CPU大部分时间 都在查Session五、Session表设计出了问题Session结构struct upf_session { uint64_t seid; uint32_t teid; pdr_t pdr; far_t far; qer_t qer; statistics_t stat; timer_t timer; };大小约256Byte现网220万Session总内存2200000 × 256B ≈ 563MB即约560MB看起来不大。但问题来了。六、CPU真正害怕的东西Cache Miss现代Xeon层级大小L132KBL21MBL330MB~60MB而Session表560MB远超L3容量。于是每个包到来TEID ↓ Hash ↓ Session Lookup ↓ DRAM访问CPU开始等待内存。七、CPU其实一直在发呆很多人认为CPU 100% 说明CPU很忙实际上CPU可能正在等待内存返回数据例如L1访问1nsL24nsL310nsDRAM80~120ns差距超过100倍于是CPU看似100% 实际上大量Cycle被浪费八、NUMA又补了一刀服务器配置2 Socket结构Socket0 ├─CPU 0~15 └─Memory0 Socket1 ├─CPU16~31 └─Memory1检查发现Worker运行在Socket0 Session分配在Socket1于是跨NUMA访问延迟从90ns变成150nsPPS进一步下降。九、更隐蔽的问题共享状态原始架构RX | ---------------- | | | W0 W1 W2 | | Global Session Table统计信息session-pkt_cnt;多个Worker同时更新。于是Cache Line竞争开始出现。即使没有显式加锁MESI协议也会导致Cache Line Ping-Pong性能进一步恶化。十、为什么Flow Affinity如此重要高性能网关最重要原则同一Flow永远属于同一个Worker例如worker_id teid % worker_num;架构变成Dispatcher | -------------------------------- | | | | | V V V V V W0 W1 W2 W3 W4这样同一个TEID 固定进入同一个Worker十一、状态本地化进一步优化不要Global Session Table改为Worker0 Session Pool Worker1 Session Pool Worker2 Session Pool Worker3 Session Pool即State Ownership原则谁处理Flow 谁拥有State这样无锁 无竞争 无跨核同步十二、Dispatcher Worker架构最终架构NIC | RX Queue | Dispatcher | ----------------------------------- | | | | | V V V V V Worker0 Worker1 Worker2 Worker3 Worker4 | | | | | | | | | | Session Session Session Session SessionDispatcher职责解析GTP-U头 提取TEID Hash分发Worker职责Session Lookup PDR匹配 FAR执行 QER处理 转发Session仅属于本Worker。十三、批处理带来的收益不要process_one_packet();要rte_eth_rx_burst();例如32 Packet一批处理。收益更高Cache命中率更少函数调用更好的流水线利用率更少Ring访问次数十四、优化后的结果优化前指标数值Worker16CPU100%PPS80万Session220万Cycle/Packet6000优化后指标数值Worker16CPU100%PPS420万Session220万Cycle/Packet1100可以看到CPU始终100% 但吞吐提升超过5倍这才是DPDK系统真实的优化效果。十五、高性能网关设计原则经过这次故障分析可以总结出高性能网关设计的五条核心原则1. Flow Affinity同一Flow固定进入同一Worker。2. State Ownership状态归属线程。3. NUMA Awareness网卡、CPU、内存保持同NUMA。4. Batch Processing尽可能批量处理数据包。5. Cache First设计首先考虑Cache命中率而不是代码优雅性。结语许多开发者认为高性能网关的瓶颈来自CPU主频、核数或者算法复杂度。但在现代DPDK系统中更常见的情况是CPU已经100%运行却有大量周期浪费在等待内存、跨NUMA访问和缓存一致性维护上。从本质上说高性能网关优化的核心并不是让CPU更忙而是让CPU把每一个Cycle都花在真正的数据处理上。当Flow、State、Cache和NUMA形成统一设计时系统才能从百万PPS平滑扩展到数百万甚至千万PPS这也是UPF、CGNAT和下一代边缘网关架构设计的关键所在。