DPDK 高性能数据面架构设计:无锁配置更新、共享状态管理与会话查询
在基于 DPDK 的高性能网络系统中,通常采用多核 Worker 独立处理报文的架构,以减少线程间的资源竞争,提高数据面的处理效率。
但在实际工程中,除了报文收发,还需要解决配置动态更新、共享状态维护、大规模会话查询等问题。
这些功能往往涉及控制面与数据面之间的数据交互。如果设计不合理,容易引入线程锁竞争、数据一致性问题,甚至影响数据面的报文处理性能。
针对这些问题,整理了三个相关的架构设计方案。
一、如何实现兼顾无锁与一致性的配置更新
在 DPDK 多核架构中,Worker 需要持续访问路由、策略、地址转换等配置数据。
如果配置面直接修改 Worker 正在使用的对象,可能产生数据竞争;如果通过读写锁保护配置,又会增加报文处理路径的同步开销。
本方案采用配置副本构建 + 原子版本发布 + Worker 主动切换的方式:
- 配置面提前构建新对象,未修改的对象继续复用。
- 通过 Release/Acquire 原子操作建立配置发布与读取之间的同步关系。
- Worker 在报文批次之间检查配置版本。
- 发现版本变化后,统一替换本地模块的配置指针。
- Worker 完成版本切换。
- 配置面等待所有相关 Worker 切换后,安全回收旧配置。
这种方式避免了 Worker 在报文处理过程中反复加锁,同时保证每个 Worker 在单个报文批次中使用完整的一组配置。
对于多 NUMA 架构,还可以为不同 NUMA 节点构建本地配置实例,降低跨节点内存访问。
二、控制面与数据面双侧更新共享状态的无锁设计
在网络系统中,ARP/NDP 邻居表等共享数据既可能由用户配置修改,也可能由 Worker 接收到的报文触发更新。
如果多个线程分别修改共享配置,即使使用原子版本号,也可能发生更新覆盖或数据不一致。
本方案采用单写入线程模型:
- 所有共享状态的实际修改由配置面更新线程统一完成。
- Worker 通过消息队列异步上报 ARP/NDP 等事件。
- 配置面基于最新状态构建新的配置对象。
- 通过版本发布与 Worker 切换机制完成配置切换。
- 所有相关 Worker 完成切换后,再回收不再使用的旧对象。
这种设计将共享状态的写操作集中管理,避免多线程同时修改同一份配置,同时保持 Worker 报文处理路径无需获取读写锁。
三、Worker 会话表如何支持配置面查询
在高性能网络系统中,每个 Worker 通常维护独立的本地会话表,以避免跨核访问和锁竞争。
但是,当用户需要查询全部连接或按照 IP、端口等条件检索会话时,就需要汇总多个 Worker 的数据。
如果直接让 Worker 遍历和复制会话,面对百万甚至千万、亿级连接时,可能明显占用报文处理时间。
本方案将会话查询工作转移到配置面:
- 每个 Worker 通过独立的 SPSC 无锁队列向配置面传递新建会话的指针。
- 配置面根据会话过期时间,将会话组织到时间轮中。
- 通过原子引用计数管理会话对象生命周期。
- Worker 仍独立维护本地会话表,不需要参与配置面的查询遍历。
- 配置面通过分批扫描控制查询与清理任务的执行开销。
该设计主要解决的是会话查询任务与 Worker 报文处理之间的资源竞争问题。
需要注意,引用计数只能保证对象生命周期安全,不能自动保证可变字段读取的一致性。此外,全量会话清理和延迟回收也需要进一步处理。
四、总结
上述三个方案分别解决了 DPDK 高性能系统中不同层面的控制面与数据面交互问题。
设计方案 |
核心目标 |
主要技术 |
共享状态无锁更新 |
避免多个线程并发修改共享状态 |
单写入线程、事件队列、版本发布 |
配置无锁热更新 |
避免配置更新影响报文处理 |
配置副本、原子操作、版本切换 |
大规模会话查询 |
减少查询任务对 Worker 的影响 |
SPSC 队列、时间轮、引用计数 |
三个方案的共同设计原则是:
尽可能将复杂操作放在控制面执行,让数据面 Worker 专注于报文处理,减少热路径上的锁竞争、跨核同步与额外计算。
这些方案适用于 DPDK 网关、负载均衡、安全网关等需要兼顾高吞吐、动态配置和运行状态查询的网络系统。
完整设计细节、架构图及相关代码可以参考上述三篇原文。