从 Rust 模块化探索到 DLB 2.0 实践|得物技术

一、前言
在云原生架构高速迭代的背景下,基础设施的性能瓶颈与安全隐患成为技术演进的关键挑战。本文系统记录了团队基于 Rust 语言改造 Nginx 组件的完整技术路径:从接触 Cloudflare 的 quiche 库,引发对 Rust 安全特性的探索,到通过 FFI 实现核心逻辑的跨语言调用;从突破传统 C 模块开发范式自研 ngx_http_rust_module SDK ,到全面采用 Pingora 框架构建新一代 DLB 2.0 流量调度平台。
实践表明,Rust 的内存安全机制与异步高并发能力可显著提升负载均衡组件的性能边界与可靠性,为超大规模流量调度场景提供全新解决方案。本技术演进过程将详述架构设计、核心模块实现及性能优化策略,为同类基础设施升级提供可复用的工程经验。
二、Nginx+Rust 的模块化探索
探索的起点源于和 quiche(cloudflare 开发的高效 quic 实现)的初次邂逅,这扇门将项目组成员引入了 Rust 语言的世界。Rust 以其卓越的内存安全、无惧并发的特性以及出色的性能潜力,迅速展示了其作为系统级编程语言的优势。这份吸引力促使我们思考:能否将 Rust 的安全与性能注入我们更广泛的基础设施中?作为核心组件的 Nginx 自然成为了探索的焦点。
我们首先聚焦于 FFI(外部函数接口)技术,通过它构建 Rust 与 C 语言的交互桥梁。借助 FFI,我们将核心业务逻辑以 Rust 实现,并将 Rust 代码编译为符合 C-ABI 规范的动态链接库。这种设计使得 Nginx 能够像调用原生 C 模块一样无缝集成 Rust 编写的库,在保障系统稳定性的同时提升性能。

采用该方案的限流模块示例如下:

鉴于单向调用模式在应用场景上的局限性,如果仅仅支持上面的单向调用流,使用的场景将大打折扣,目前 Nginx 中大量的功能以三方模块的形式呈现,C module 的开发难度较高,需要理解的组件概念颇多,团队尝试开发了 ngx_http_rust_module 模块作为一个探索期的折中方案。

ngx_http_rust_module 本质上是一个 Rust SDK,是对传统 C 模块开发模式的一种现代化补充尝试。SDK 层封装好的胶水 function 极大便利了 rust module 上层开发,可以实现纯 Rust 编码来实现业务功能,实践验证具备较高工程价值。
目前已封装的部分 SDK 展示以及设置响应 header 方法示例:


三、全面拥抱 Rust 进入 DLB2.0 阶段
完成 Nginx 模块的初步探索后,团队技术路线转向 Cloudflare 开源的 Pingora 框架,该高性能 Rust 框架专为构建可编程、高可靠的流量调度平台而设计。
核心优势
云原生架构 :通过异步任务调度消除 Nginx 的进程隔离瓶颈,实现 CPU 负载均衡与高效连接复用。
性能突破 :实测每秒可处理 10 万请求,资源消耗降至传统方案的三分之一。
协议生态 :原生支持 HTTP/1-2、Websocket 端到端代理。
安全演进 :基于 Rust 内存安全特性,集成 FIPS 认证的加密模块解决 C/C++方案的安全隐患。
扩展能力 :提供可编程负载均衡 API 与热升级机制,满足超大规模流量调度需求。
在原型验证其技术可行性之后,团队决定在该框架骨干上构建了 DLB 2.0 产品体系:

核心能力设计
声明式配置管理提供基于 YAML 的声明式配置接口,显著提升配置可读性与维护效率。支持热加载机制,实现流量无损的配置更新,彻底规避传统代理重载导致的 503 服务中断。
流量处理支持单一端口多域名 TLS 证书托管能力,简化 HTTPS 服务部署。提供与 Nginx 完全兼容的 server/path 路由匹配逻辑,确保无缝迁移。实现路径重写引擎,满足复杂流量调度需求。采用模块化 Filter 链设计,支持按需插拔流量处理组件。
服务发现集成静态资源配置与动态 DNS 服务发现双模式。支持 sylas 注册中心。企业级监控。提供增强型访问日志。输出完全兼容 DLB 1.0 的监控指标(VTS 格式)。保留流量录制数据规范,确保监控体系平滑升级。
每个模块的设计均遵循"高内聚低耦合"原则,在保障生产环境稳定性的前提下,为超大规模流量调度场景提供可扩展的技术支撑,后续将逐一拆解部分关键模块的技术实现细节与性能优化策略。
配置体系
静态配置
DLB 2.0 在配置层面按类型拆分成多个细粒度的 yaml 文件,其中最核心的是 server.yaml 以及 upstream.yaml ,为了对标 Nginx 核心概念、这部分不引入新的术语,继续沿用 server 、 location 、 upstream 三大基础模块。
通过 server 模块声明虚拟主机,支持多域名监听及端口绑定,兼容 server_name 的泛域名解析能力,同时实现单端口多域名 TLS 证书的精准匹配。
location 模块完整继承 Nginx 的路径匹配逻辑(含精确匹配 = 、正则匹配 ~ 等模式),支持基于路径的请求路由与正则表达式重写规则,确保策略迁移的零成本适配。同时支持 proxy_pass 、 if 、 proxy_headers 、 return 等核心指令。
upstream 动态服务发现机制支持权重负载均衡,通过 YAML 结构化配置实现后端集群的声明式管理,并与 DNS 的服务发现深度集成,彻底消除传统配置中硬编码 IP 的维护负担。
配置解析
在 DLB 2.0 的配置模型中, server 、 location 、 upstream 三者构成层次化路由架构:

server 作为虚拟服务单元,通过 Vec<LocationConf> 聚合任意数量的 location 路由规则。
location 作为请求路径处理器,可独立关联至不同的 upstream 服务组。
upstream 采用原子引用计数机制( Arc )封装配置,通过 Arc::strong_count() 实时监控引用状态,避免冗余配置拷贝,基于 Rust 的并发安全特性,最终设计为 Arc<Mutex<UpstreamConf>> 结构: Mutex 保障多线程环境下的内部可变性,支撑配置热更新需求。 Arc 维持跨线程的只读共享能力,确保访问高效性。
main thread 解析完 server.yaml 与 upstream.yaml 后,将生成两个核心哈希映射:
server 配置映射表:关联域名与路由规则集。
upstream 线程安全容器:托管负载均衡服务组状态。
运行时配置转化
上述的 ServerConf 与 UpstreamConf 面向的是用户,特点是易于理解与维护、支持 YAML 反序列化。
而为了专注运行时效率(比如负载均衡策略中的字符串转化为枚举类型),我们会将 UpstreamConf 转化为 RunTimeUpStream 结构, ServerConf 同理。
转化之后得到全局唯一的 GlobalConf :
流量处理
域名匹配
如果仅有上面的 runtime_servers 这一个哈希表,还不能实现复杂的 Nginx 域名匹配规则,Nginx 域名匹配的优先级机制包括:精确匹配>前置通配符>正则匹配(后置通配符在 1.0 版本未使用,暂且忽略),为了确保无缝迁移,需要提供与 Nginx 完全兼容的 server 匹配逻辑,考虑到代码可维护性,可以这样组织运行时数据:
为精确域名使用 HashMap,实现 O(1)查找。
前置通配符匹配存储为 Vec,且确保最长匹配优先。
正则表达式只能顺序匹配,保持 Vec<Regex>原顺序。
最终得到这样的结构体:
其中需要留意的是成员 prefix_nested_map ,为了确保最长匹配优先,我们将 prefixes: Vec<(String, String)> 转化为了 NestedHashMap 结构, NestedHashMap 为一个嵌套哈希结构,可基于域名分段实现高效检索。
路由匹配
讲完了域名匹配,我们再深入路由匹配,在开始之前,我们先回顾一下 Nginx 的 location 指令。
location 通常在 server{} 块内定义,也可以嵌套到 location{} 内,虽然这不是一种推荐的配置方式,但它确实是被语法规则支持的, localtion 语法主要有如下几种形式:
※ 修饰符语义及优先级(依匹配精度降序排列)
= :精确匹配(Exact Match),URI 必须与模式完全一致时生效(最高优先级)。
^~ :最佳前缀匹配(Prefix Match),选中最长非正则路径后终止搜索(优先级次于=)。
~ :区分大小写的正则匹配(Case-Sensitive Regex)。
~* :不区分大小写的正则匹配(Case-Insensitive Regex)。
@ :内部定位块(Named Location),仅限 try_files 或 error_page 指令调用,不对外暴露。
Nginx 在解析完 location 之后会进行一系列的工作,主要包括:
分类: 根据 location 的修饰符参数标识不同的类型,同时去除 name 前面的修饰符
排序: 对一个 server 块内的所有 location 进行排序,经过排序之后将 location 分为了 3 类通用类型,通用类型的 location 将建立一棵最长前缀匹配树正则类型,顺序为配置文件中定义的顺序,正则会用 pcre 库先进行编译内部跳转类型,顺序也为配置文件中定义的顺序
拆分:将分类的 3 种类型拆分,分门别类的处理
其中最复杂的是最长前缀匹配树的构建,假设 location 规则如下,构造一棵最长前缀匹配树会经过如下几个步骤:

把 locations queue 变化 locations list,假设一个 location 的 name 是 A 的话,所有以 A 前缀开头的路由节点都会放到 A 节点的 list 里(最长前缀匹配)。

2.按照上述步骤递归初始化 A 节点的所有 list 节点,最终得到下面的 list。

3.在上述创建的 list 基础上,确定中间节点,然后从中间节点把 location 分成两部分,然后递归创建左右子树,最后处理 list 队列,list 队列创建的节点会加入到父节点的 tree 中,最终将生成一棵多叉树。

现在你应该已经明白了最长前缀匹配树的构建流程,让我们回到 2.0 的设计上来,这部分同样维护了三个结构分别对应精确匹配、正则匹配以及最长前缀匹配。
精确匹配、正则匹配比较简单,我们重点介绍最长前缀匹配,最长前缀匹配树的构建基本上是把 Nginx 代码原原本本的翻译过来,通过 create_list() 分组节点、 create_tree() 生成多叉树。通过 find_location 遍历树结构查找最长有效路径,其中路径比较函数 path_cmp() 确保按字典序定位子树,匹配成功时返回( need_stop, location ),其中 need_stop 标志是否中止搜索(模拟 ^~ 行为)。
路由重写
路由重写是实现请求路径动态转换的核心能力,在语义层面,我们完全兼容 Nginx 的配置语义。
regex replacement [flag] ,同时采用预编译正则引擎,在路由加载期完成规则编译。
模块化 Filter 链
Pingora 引擎已经将请求生命周期划分了足够细的各个阶段,为了更精细化控制同一 phase 执行的各个 Filter,可通过自定义的 ProxyFilter trait,与 Pingora 引擎的 phase 关联起来。
ProxyFilter 主要包含四个方法:
phase : Filter 的执行阶段, 生命周期阶段锚点,可以根据实际需要进行扩展插入更细粒度的阶段进行请求处理。
name : Filter 的名称。
order : 在同一个 phase 内 Filter 的执行顺序。
handle : Filter 的执行逻辑,若返回的是 HandleResult::Continue ,则表示当前 filter 执行完成,继续执行下一个 filter,否则停止 filter chain 的执行动作。
目前我们已经实现的 Filter 包括但不限于:

四、总结
作为《从 Rust 模块化探索到 DLB 2.0 实践》系列的第一篇,本文介绍了开发 DLB 2.0 的背景以及详述了 DLB 2.0 如何通过声明式配置管理、分层路由架构及与 Nginx 完全兼容的匹配逻辑,实现亿级流量调度场景下的高可用与零迁移成本。
当前成果验证了 Rust 在负载均衡产品中改造中的工程价值:依托线程安全的运行时结构(如 Arc<Mutex<T>> )、高效前缀树路由( HostSelector )及最长前缀匹配,性能与可维护性均突破传统方案边界。
在后续篇章中,我们将继续深入剖析服务发现、监控与日志等核心模块,为超大规模云原生架构提供完整的参考实践。
往期回顾
1.eBPF 助力 NAS 分钟级别 Pod 实例溯源|得物技术
2.正品库拍照 PWA 应用的实现与性能优化|得物技术
3.汇金资损防控体系建设及实践 | 得物技术
4.得物社区活动:组件化的演进与实践
5.从 CPU 冒烟到丝滑体验:算法 SRE 性能优化实战全揭秘|得物技术
文 / 雷泽
关注得物技术,每周更新技术干货
要是觉得文章对你有帮助的话,欢迎评论转发点赞~
未经得物技术许可严禁转载,否则依法追究法律责任。
评论