资讯动态

用 Rust 写生产级服务要踩多少坑——Cloudflare 把答案做成了一个开源库

发布时间:2026/9/11 19:21:12 来源:尧图企业网站定制
写一个在本地跑起来的程序和把这个程序部署成全球分布式服务是两件本质上不同的事情。很多工程师都经历过这个阶段一个在笔记本上表现良好的原型一旦进入生产环境就开始暴露出各种问题——日志不够用、配置改起来麻烦、崩溃了不知道哪里出问题、安全加固一点没做。Cloudflare 的工程师在开发了大量 Rust 服务之后把反复解决这些问题的经验提炼成了一个库叫做Foundations。2024 年初这个库正式开源。原文链接https://blog.cloudflare.com/introducing-foundations-our-open-source-rust-service-foundation-library/开源地址https://github.com/cloudflare/foundationsAPI 文档https://docs.rs/foundations/latest/foundations/从 Oxy 里长出来的基础设施Foundations 最初是 Cloudflare 的 Oxy 代理框架的一部分。Oxy 是他们用 Rust 重写代理层的核心项目在构建过程中积累了大量对每个生产级 Rust 服务都需要什么这个问题的实践答案。后来团队意识到这些能力不只对 Oxy 有价值对 Cloudflare 内部所有的 Rust 服务都适用于是把它们抽离出来做成了独立的库。D1、Constellation 等服务都在用它。从笔记本到生产环境差的是什么Cloudflare 团队总结了从原型到生产的三大核心差距可观测性在本地开发者有 IDE、调试器、printf。但当服务跑在全球数千台远程服务器上这些工具都不再适用。你需要结构化的日志、分布式追踪、指标监控而且这套系统本身必须低摩擦否则开发者会绕过它。配置管理本地原型往往用硬编码或简单的命令行参数。生产环境需要支持复杂的层级配置、动态修改、以及配置即代码的范式。安全本地运行的程序不需要考虑太多外部威胁。生产环境的服务暴露在各种攻击面前系统调用层面的沙箱、最小权限原则都是必要的基础设施。Foundations 就是针对这三个维度系统性地给出答案。设计原则能用比能调节更重要Foundations 在设计上坚持三个原则高度模块化很多服务比 Foundations 出现得更早不可能要求所有人同时切换。因此 Foundations 的每个功能都可以独立引入团队按自己的节奏迁移。API 人体工学大量使用 Rust 的过程宏让开发者少写样板代码把文档和定义放在同一个地方。开箱即用优先Foundations 的目标是让工程师几乎不花时间做基础设施配置加进来就能用之后再慢慢调整细节。这是一个有意识的取舍——相比极度灵活的通用配置 API它针对生产验证过的特定环境进行了优化。遥测Telemetry日志、追踪、指标三合一Foundations 把可观测性的三个维度统一在一套 API 里用TelemetryContext对象作为隐式的上下文载体贯穿始终。Tracing链路追踪Foundations 的追踪 API 在接口风格上接近 tokio/tracing支持隐式上下文传播和 Future 包装。但它在几个地方做了自己的扩展采样率动态覆盖全局采样率保持低值来控制性能开销但在调试特定账号、特定连接的性能问题时可以在代码里局部覆盖采样率强制对这部分流量做完整采样。这在 tokio/tracing 里需要额外的工作。跨服务追踪拼接上游服务可以把采样决策传递给下游整条链路的追踪数据会自动关联成一个完整视图而不是散落在各个服务的日志里。Trace Forking追踪分叉这是解决一个具体工程问题的设计——对于长连接上的多路复用请求比如 HTTP/2 或 HTTP/3如果所有请求共享同一条 Trace分析起来会非常混乱。Foundations 允许为每个请求创建独立的子 Trace同时保持与父连接 Trace 的关联两者都可以独立分析也可以合并观察。#[tracing::span_fn(respond to request)]asyncfnrespond(endpoint_name:ArcString,req:RequestBody,routes:ArcMapString,ResponseSettings,)-ResultResponseBody,Infallible{// 业务逻辑}Logging日志Foundations 的日志系统解决了一个在复杂服务里很常见的痛点层级上下文的传递。典型场景一个 TCP 连接上有多个 HTTP 请求。日志里需要同时携带连接 ID、协议版本连接级别的信息以及请求 URL请求级别的信息。传统做法是为每个请求创建一个新 logger手动复制连接 logger 的标签然后显式传递这个 logger 对象。这很繁琐而且会让业务逻辑代码到处都是与遥测无关的参数传递。Foundations 的解法是借用 tokio/tracing 对 Future 的 instrumentation 机制把日志上下文做成隐式传播的letconn_tele_ctxTelemetryContext::current();leton_requestservice_fn(move|req|{// 每个请求得到独立的日志继承自连接日志// 和独立的 Trace链接到连接 Traceconn_tele_ctx.with_forked_log().with_forked_trace(request).apply(asyncmove{respond(endpoint_name,req,routes).await})});with_forked_log()创建了一个继承自连接上下文的新日志上下文并自动传播到整个请求的异步调用链中不需要显式传参。Metrics指标Foundations 使用 Prometheus 官方 Rust 客户端并在上面加了一个过程宏来减少样板代码。定义带标签的指标只需要这样写#[metrics]pub(crate)modhttp_server{/// 活跃客户端连接数。pubfnactive_connections(endpoint_name:ArcString)-Gauge;/// 失败连接总数。pubfnfailed_connections_total(endpoint_name:ArcString)-Counter;/// HTTP 请求总数。pubfnrequests_total(endpoint_name:ArcString)-Counter;/// 失败请求总数。pubfnrequests_failed_total(endpoint_name:ArcString,status_code:u16)-Counter;}文档注释直接成为指标的说明文档标签类型在编译期检查不需要手动注册或管理生命周期。内存分析jemalloc 集成对于长期运行的服务jemalloc 在内存分配效率上通常优于系统默认分配器。Foundations 封装了 jemalloc 的集成并把它的内存分析能力包装成安全的 Rust API方便在遇到内存增长异常时直接介入分析。安全用 seccomp 把系统调用锁进笼子Foundations 提供了一套对seccompLinux 内核的系统调用过滤机制的封装。seccomp 允许为进程定义一张系统调用白名单任何不在白名单上的系统调用被执行时内核可以选择杀死进程、返回错误、或者记录日志。这对于防御任意代码执行类攻击是一道有效的纵深防线——即使攻击者利用漏洞在你的进程里执行了代码大量危险的系统调用也会被直接拦截。Foundations 的 API 设计力求简洁usefoundations::security::common_syscall_allow_lists::{ASYNC,NET_SOCKET_API,SERVICE_BASICS};usefoundations::security::{allow_list,enable_syscall_sandboxing,ViolationAction};allow_list!{staticALLOWED[..SERVICE_BASICS,// 基础服务所需的系统调用..ASYNC,// 异步运行时所需的系统调用..NET_SOCKET_API// 网络服务所需的系统调用]}enable_syscall_sandboxing(ViolationAction::KillProcess,ALLOWED)..SERVICE_BASICS这样的展开语法允许组合多个预定义白名单同时支持逐条添加自定义的系统调用。Foundations 内置了几组常用场景的白名单绝大多数服务直接组合使用即可。配置与 CLIRust 结构体就是配置文档Foundations 的配置管理方案有一个核心理念服务应该自带一份可以直接运行的默认配置而不是让用户在空配置文件面前无从下手。实现方式是把配置直接定义为 Rust 结构体默认值写在代码里文档注释自动变成配置说明#[settings]pub(crate)structHttpServerSettings{/// 遥测配置。pub(crate)telemetry:TelemetrySettings,/// HTTP 端点配置。pub(crate)endpoints:MapString,EndpointSettings,}运行 CLI 的--print-config命令Foundations 会把整个配置结构渲染成一份带注释的 YAML 文件---# 遥测配置。telemetry:# 分布式追踪设置tracing:# 是否启用追踪。enabled:true# Jaeger Thrift (UDP) 代理地址。jaeger_tracing_server_addr:127.0.0.1:6831# 采样率0.0 到 1.0 之间1.0 表示全量采样。sampling_ratio:1.0# 日志设置。logging:output:terminalformat:textverbosity:INFO# HTTP 端点配置。endpoints:Example endpoint:addr:127.0.0.1:0routes:/hello:status_code:200response:World这份 YAML 的每一个字段都附有从代码注释生成的说明。用户拿到这份文件改掉需要修改的部分不需要翻文档就能理解每个选项的含义。这套思路的普遍价值Foundations 解决的问题不是 Cloudflare 独有的而是每个在生产环境运行 Rust 服务的团队都会遇到的。它背后有一个值得记住的工程判断把基础设施能力和业务逻辑彻底分开。日志应该自动传播配置应该自文档系统调用应该默认受限——这些能力应该像空气一样存在工程师不需要每次写新服务都重新把轮子造一遍。Foundations 目前在 Cloudflare 内部的多个核心服务上运行API 文档和示例代码都已随库一起开源任何 Rust 项目都可以按模块独立引入。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价