当前位置: 首页 > news >正文

从100万PPS到10万PPS:一次高性能网关性能雪崩的根因分析与架构重构

一、问题背景

某运营商边缘节点部署了一套用户面网关。

功能非常简单:

N3接口 | | [ Gateway ] | | N6接口

核心流程:

收包 ↓ 会话查找 ↓ 策略匹配 ↓ 转发

实验室压测数据:

指标数值
CPU32 Core
流量64B
PPS100万
时延<100us

看起来非常健康。

然而上线后不到两天,出现严重告警:

CPU达到95% 业务时延超过20ms 丢包率超过15%

更奇怪的是:

流量只有12万PPS

按理说距离实验室极限还有很大空间。

为什么会出现这种情况?


二、第一步定位:CPU到底在干什么

首先查看系统CPU。

top

发现:

user: 85% sys : 12% idle: 3%

说明CPU确实被消耗掉了。

继续:

perf top

热点函数如下:

__netif_receive_skb ip_rcv ip_forward fib_lookup nf_hook_slow

占比超过70%。

这意味着:

CPU大量时间消耗在Linux协议栈

而不是业务逻辑。


三、Linux协议栈为什么慢

很多开发者认为:

转发包 ≈ memcpy

实际上完全不是。

一个数据包从网卡到应用层大致经过:

NIC ↓ DMA ↓ RX Ring ↓ 硬中断 ↓ 软中断 ↓ SKB创建 ↓ 协议栈解析 ↓ 路由查找 ↓ Netfilter ↓ Socket ↓ 用户态

技术插图:

每一步都存在:

Cache Miss 内存访问 上下文切换 锁竞争

尤其SKB。

Linux协议栈中的SKB:

struct sk_buff { ... };

长度超过300字节。

每个包都需要:

申请SKB 初始化SKB 释放SKB

在百万PPS场景下:

每秒百万次内存管理

开销极其巨大。


四、为什么实验室没问题

继续分析流量模型。

实验室:

100万PPS 1000条会话

现网:

12万PPS 200万条会话

这里出现关键差异:

会话规模

五、真正的问题:Cache Miss

会话表结构如下:

struct session { uint64_t seid; uint32_t teid; policy_t *policy; qos_t qos; statistics_t stat; };

总大小约:

256 Bytes

200万条会话:

256 × 2000000 ≈ 512MB

远超CPU Cache。

现代CPU:

L1 Cache 32KB L2 Cache 1MB L3 Cache 30~60MB

而会话表:

512MB

根本放不进去。

结果:

每个包:

Hash ↓ 查表 ↓ 内存访问

几乎都会触发:

LLC Miss

六、如何证明是Cache问题

perf继续分析:

perf stat

结果:

cache-misses cache-references LLC-load-misses

异常高。

进一步:

perf record perf report

热点:

rte_hash_lookup()

占比高达40%。

终于定位:

CPU不是在处理包 而是在等内存

七、NUMA带来的第二次伤害

服务器配置:

2 Socket

NUMA结构:

Socket0 ├ CPU0-15 └ Memory0 Socket1 ├ CPU16-31 └ Memory1

技术插图:

发现:

线程运行在Socket0 会话内存分配在Socket1

于是每次查表:

远程内存访问

延迟增加:

80ns → 150ns+

看似只增加几十纳秒。

但:

100万PPS

累计后就是大量CPU时间浪费。


八、锁竞争开始出现

继续观察:

perf lock report

热点:

pthread_spin_lock

统计模块代码:

session->pkt_cnt++; session->byte_cnt += len;

多个线程共享会话。

因此:

lock ↓ update ↓ unlock

技术插图:

高并发时:

CPU不停争抢Cache Line

形成:

Cache Ping-Pong

九、架构问题浮出水面

原设计:

RX ↓ Worker1 Worker2 Worker3 Worker4 ↓ 共享Session表

问题:

所有线程访问同一份状态

意味着:

锁 Cache同步 NUMA访问

全部出现。


十、高性能网关的正确设计

核心原则:

让数据跟着CPU走 不要让CPU追着数据跑

第一原则:Flow Affinity

同一流量固定进入同一个Worker。

例如:

TEID Hash
worker_id = teid % worker_num;

架构:

Dispatcher | ------------------------- | | | | V V V V W0 W1 W2 W3

同一个TEID永远进入同一个线程。

好处:

无锁

第二原则:状态本地化

不要:

全局Session表

改成:

Worker0 Session Worker1 Session Worker2 Session Worker3 Session

即:

状态归属线程

这样:

无需加锁

第三原则:批处理

不要:

recv(); process(); send();

改成:

rte_eth_rx_burst();

例如:

32 packets

一起处理。

优势:

减少函数调用 提高Cache命中率 提升指令流水线利用率

第四原则:避免共享统计

不要:

global_counter++;

改成:

Per-Core Counter

每核维护:

local_counter

定期汇总。


第五原则:NUMA感知

绑定:

网卡0 ↓ Socket0 ↓ Worker0~15 网卡1 ↓ Socket1 ↓ Worker16~31

原则:

CPU Memory NIC 必须同NUMA

十一、重构后的架构

最终采用:

+------------+ | Dispatcher | +------------+ | --------------------------------- | | | | | V V V V V Worker Worker Worker Worker Worker | | | | | | | | | | Session Session Session Session Session

特点:

Dispatcher

负责:

解析头部 提取TEID Hash分发

不做业务。

Worker

负责:

会话查找 策略执行 转发

Session

归属Worker。

无锁 无共享

十二、最终性能结果

优化前:

指标数值
PPS12万
时延20ms
丢包15%

优化后:

指标数值
PPS180万
时延110us
丢包0

提升:

15倍以上

而业务逻辑几乎没有变化。

变化的只是:

架构 缓存利用 线程模型 内存布局

结语

高性能网关开发中,最大的性能瓶颈往往不是算法复杂度,而是数据访问方式。实际工程里,性能雪崩通常来自三个方面:

  1. Cache Miss导致CPU长期等待内存;
  2. NUMA跨节点访问导致延迟放大;
  3. 共享状态和锁竞争导致多核扩展性失效。

对于5G UPF、CGNAT、DPI、边缘网关等系统,真正有效的设计思路并不是不断优化代码细节,而是在架构层面坚持几个原则:

Flow Affinity State Locality Batch Processing Per-Core Design NUMA Awareness

当数据始终停留在本地CPU缓存中时,系统才能真正发挥现代多核服务器和DPDK框架的性能潜力。这也是高性能网络设备从几十万PPS迈向千万PPS最关键的一步。

http://www.jsqmd.com/news/954410/

相关文章:

  • FPGA上跑通USB转串口的Verilog工程,带全套Quartus编译中间文件
  • 2026花都区专利代理TOP3测评|专利补贴新政全解析、汽车零部件皮具美妆智造资助标准、空港经济科创扶持、高企专精特新申报加分、全年申报批次流程、专利避坑指南与本土制造企业落地案例大全 - 资讯速览
  • 政务系统中的可预测ID模式与IDOR漏洞实战分析
  • Altium Designer绿色报错别头疼,这几个隐藏快捷键和设置项才是关键
  • 你的品牌在AI搜索中排第几?用GEO评估工具测一测
  • 如何将大视频文件缩小90%:终极免费压缩工具完整指南
  • 2026 诸城防水补漏哪家好?住建实地测评权威榜单 TOP5|南部马耳山低山丘陵 / 中部缓岗坡地 / 北部潍河冲积平原、诸城经开区渗漏修缮白皮书(6 月专项调研 - 苏易修缮
  • 航空运维大模型人工智能AI系统软件平台设计方案
  • 新手福音,用快马平台AI生成代码学习ok影视配置接口开发
  • 别再手动画图了!用QGIS 3.28把Excel里的气象站点数据一键变成专业色斑图
  • Whisper语音识别轻量化微调与跨平台部署工具集(Android/Windows/服务端全支持)
  • 手机拍照为什么四角会发暗?深入聊聊ISP里的LSC模块与模组一致性校准
  • GNSS信号频点命名的秘密:从L波段到‘无线电窗口’,一次讲清导航信号为什么选这个频率
  • MuleSoft+LangChain企业级AI编排实战:数据集成与大模型协同
  • Arthas 最常用命令速查表
  • 2026快手怎么去水印?快手官方去水印途径与合规方法汇总
  • 给TMS320F28379D新手:手把手教你配置外部GPIO中断(附代码避坑)
  • MATLAB版DTLZ多目标测试函数全集(含9个标准函数+8种前沿形态变体)
  • Java后端做RAG:从4步入门到文档入库实战
  • 2026实测豆包即梦图片水印去除方法!即梦水印能去掉吗合规去除教程
  • 从H.264宏块到H.265 CTU:视频编码的“乐高积木”进化史
  • Altium Designer新手必看:PCB设计里那些烦人的绿色报错,到底怎么一键搞定?
  • [智能体-255]:Retriever:RAG 核心底座、实现各类 RAG 的统一标准组件
  • 航空制造大模型人工智能AI系统软件设计方案
  • 应用型AI落地实战:从Web服务思维到物理世界系统工程
  • LangChain实战入门:从零搭建可运行可修改的AI聊天机器人
  • 别再死记公式了!用Python+Matplotlib可视化理解吸收率、反射率和透射率
  • 靠谱的运营公司对于企业的发展起着至关重要的作用
  • 工程师如何用AI“伪造”高影响力痕迹?(非黑产,是合规影响力工程,含GitHub/Confluence/钉钉实操模板)
  • 深入glibc源码:图解_dl_fixup如何解析动态链接函数(附ret2dlresolve利用原理)