N.04 / SYSTEMS
Sodama 数据面工程笔记
一次生产级数据面 Rust 重写的完整工程记录:内核边界、map 契约、性能修复、长期稳定性与证据范围。
一次生产级数据面 Rust 重写的工程笔记:内核边界、map 契约、可复现证据,以及让一个系统长期保持健康所需的、并不光鲜的工作。
01 / 问题在边界,不在语言。
只有改变系统被理解、被验证的方式,重写才有意义。Sodama 从三个约束开始:数据面必须端到端使用 Rust;规则编译必须能与 Go 参考实现逐字节对拍;任何性能或稳定性结论都必须有可以复现的实验。
最终成果既是一套经过生产环境验证的网络系统,也是一份关于 Linux 内核路径、eBPF 生命周期、协议行为与资源治理的研究档案。
02 / 一条数据面,8 个程序,14 张 map。
内核侧包含 7 个 TC classifier 与 1 个 sk_skb offload verdict 程序,由 14 张 BTF map 携带共享状态。Rust / aya-ebpf 定义程序,Rust + aya 在用户态加载并协调它们。
数据包决策留在内核附近,配置、协议实现与运维则进入可测试的控制面。map 布局与单写者纪律被当作架构契约,而不是可以随时改动的实现细节。
03 / 复杂性由控制面承担。
用户态解析完整 .dae 配置并编译规则,实现 DNS 缓存、TCP、regex 与 EDNS 行为,以及 TCP relay、UDP NAT、TCP offload、健康探测、组策略、失败切换、状态面与指标导出。
协议工作覆盖 Shadowsocks、Trojan、VLESS、VMess、TUIC、Hysteria2 与 AnyTLS,包括 UDP 和会话行为。TLS SNI / HTTP Host 嗅探保留域名信息,交由远端解析。每个功能都放在完整数据路径中验收,而不是孤立地勾选清单。
04 / 性能,先把等待时间测出来。
一次重载不应继承慢速订阅拉取的延迟。将拉取与重载解耦后,面板回执从观测到的 19–34 秒降至 0.74 秒,验证、落盘与回执契约保持不变。
实验室仍由 netns 拓扑、故障注入、跨实现客户端、长稳压测与逐字节对拍构成。路径变快,只有在相同契约继续通过时才有意义。
| 单元测试 | 268 / 268 | 7 ignored |
|---|---|---|
| 标准 / 单臂拓扑 | 9 / 9 · 9 / 9 | netns 验证 |
| DNS / 转发面 / 运维 | 6 / 6 · 6 / 6 · 5 / 5 | 专项测试 |
| 协议梯 | 17 / 17 | 15 个会话 |
| Go 规则编译器对拍 | 5 / 5 | 逐字节一致 |
| 内核矩阵 | 9 / 9 · 9 / 9 | 宿主 + QEMU |
05 / 稳定,看长窗口里有没有漂移。
真实负载暴露了功能测试无法覆盖的问题:健康探测阻塞、握手线程失控、粘滞 IP 只作废却不重解析,以及热重载被慢速网络读取拖住。
加入硬超时、探测并发上限、拨号看门狗、拨号时解析与按源隔离失败后,3 个月长期运行测试中 RSS 常驻 68 MB,未观察到内存增长或内存跳变。
| 553 个阻塞接收线程 | Socket 超时 + 探测上限 96 + 拨号看门狗 | 长期窗口内未观察到复发 |
|---|---|---|
| Relay 线程 36 | 组拨号回归 Reactor | Relay 线程 0;斜率 +2.39 → −0.11 |
| 粘滞地址漂移 | 拨号时解析,按源保留健康成员 | 自愈且不丢来源 |
| 重载回执 | 19–34 秒 → 0.74 秒 | 经过验证、落盘与重载后的面板回执;不等于完整请求延迟。 |
|---|---|---|
| RSS | 68 MB | 3 个月长期运行测试、单一生产负载。 |
| 单元测试 | 268 / 268 · 7 ignored | 对应特定修订;测试通过不等于覆盖所有路径。 |
| 尚未公开 | 硬件 · 内核版本 · 提交 SHA · 流量画像 · 完整采样日志 | 在把这些数字当作通用基准之前,需要补齐。 |
03′ / 不改引擎的控制台。
控制台使用 Axum 后端与 Leptos → WASM 前端,交付为单文件 musl 二进制。它读取既有 Unix socket 状态与指标契约;配置修改统一经过验证、落盘与热重载回执。
二进制约 2.6 MB,静态资源约 940 KB;无浏览器访问时约 3 MB RSS、3 个线程。运行时卡片展示 RSS、线程、FD、CPU 与连接,数据新鲜度指示则区分“面板卡住”与“引擎本来就稳定”。
06 / 决策必须带修订条件。
63 条已采纳 ADR 记录背景、选项、理由、后果,以及最重要的——什么测量结果会触发修订。档案从语言迁移、map 布局延伸到 M4 reactor 与零线程握手。
剩余 backlog 同样明确:工程化打包与发行 CI、fuzz、更多 eBPF 挂载模式、工具链供应链固定,以及协议覆盖。稳定不代表完成,而代表系统行为仍在被持续测量。
内存安全只是起跑线。运维安全,是知道每一个线程、描述符、截止时间与失败路径。