Fastrace:Rust 中分布式追踪的现代化方案

news2025/5/25 21:14:02

原文链接:Fastrace: A Modern Approach to Distributed Tracing in Rust | FastLabs / Blog

摘要

在微服务架构中,分布式追踪对于理解应用程序的行为至关重要。虽然 tokio-rs/tracing 在 Rust 中被广泛使用,但它存在一些显著的挑战:生态系统碎片化、配置复杂以及高开销。

Fastrace 提供了一个可用于生产环境的解决方案,具有无缝的生态系统集成、开箱即用的 OpenTelemetry 支持,以及更简洁的 API,能够自然地与现有的日志基础设施协同工作。

以下示例展示了如何使用 fastrace 对函数进行追踪:

#[fastrace::trace]
pub fn send_request(req: HttpRequest) -> Result<(), Error> {
    // ...
}

Fastrace 已在如 ScopeDB 等产品中投入生产使用,帮助追踪和调试 PB 级的可观测性数据工作负载。

为什么分布式追踪很重要

在当今的微服务和分布式系统中,了解应用程序内部发生的事情变得前所未有地困难。一个用户请求可能在完成之前涉及数十个服务,传统的日志记录方法很快就会显得不足。

考虑一个典型的请求流程:

用户 → API 网关 → 认证服务 → 用户服务 → 数据库

当发生异常或应用程序性能不佳时,问题究竟发生在哪里?单个服务的日志只显示了追踪的片段,缺乏整个系统中请求流动的关键上下文。

这使得分布式追踪变得至关重要。追踪创建了一个跨服务边界的请求流程的连接视图,使得能够:

  • 识别跨服务的性能瓶颈
  • 调试组件之间的复杂交互
  • 了解依赖关系和服务关系
  • 分析延迟分布和异常值
  • 将日志和指标与请求上下文相关联

一个常见的方案:tokio-rs/tracing

对于一些 Rust 开发者来说,tokio-rs/tracing 是实现追踪的首选解决方案。以下是一个典型例子:

fn main() {
    // 初始化 tracing 订阅者
    // 省略复杂的配置代码...
    // 创建一个 span 并记录一些数据
    let span = tracing::info_span!("processing_request",
        user_id = 42,
        request_id = "abcd1234"
    );
    // 进入 span(为当前执行上下文激活它)
    let _guard = span.enter();
    // 在 span 上下文中记录日志
    tracing::info!("Starting request processing");
    process_data();
    tracing::info!("Finished processing request");
}

tokio-rs/tracing 提供了过程宏简化函数插桩:

#[tracing::instrument(skip(password), fields(user_id = user.id))]
async fn authenticate(user: &User, password: &str) -> Result<AuthToken, AuthError> {
    tracing::info!("Authenticating user {}", user.id);
    // ...更多代码...
}

tokio-rs/tracing 的问题

根据我们的用户体验,tokio-rs/tracing 存在几个显著的问题:

1. 生态系统碎片化

tokio-rs/tracing引入自己的日志宏,与使用标准 log crate 的代码产生了分歧:

// 使用 log crate
log::info!("Starting operation");
// 使用 tracing crate(不同的语法)
tracing::info!("Starting operation");

这种碎片化对库作者尤其成问题。在创建库时,作者将面临一个困难的选择:

  • 使用 log crate,以兼容更广泛的生态系统
  • 使用 tokio-rs/tracing,以获得更好的可观测性功能

许多库为了简单选择了第一种方式,但错过了追踪的好处。

虽然 tokio-rs/tracing 提供了一个 log 特性标志,允许在使用 tokio-rs/tracing 的宏时向 log crate 发出日志记录,但库作者必须手动启用这个特性,以确保所有用户无论使用哪种日志框架都能正确接收日志记录。这为库维护者带来了额外的配置复杂性。

此外,使用 tokio-rs/tracing 的应用程序还必须安装和配置 tracing-log 桥接器,以正确接收使用 log crate 的库的日志记录。这造成了一个需要显式配置的双向兼容性问题:

# Library's Cargo.toml
[dependencies]
tracing = { version = "0.1", features = ["log"] }  # Emit log records for log compatibility

# Application's Cargo.toml
[dependencies]
tracing = "0.1"
tracing-log = "0.2"  # Listen to log records for log compatibility

2. 对库的性能影响

库的作者对性能开销特别敏感,因为他们的代码可能会在循环或性能关键路径中被调用。当使用 tokio-rs/tracing 进行检测时,其开销可能相当显著,这带来了一个两难的选择:

  • 始终进行追踪检测 —— 这样会对所有用户都带来额外的性能开销。
  • 完全不进行检测 —— 这样会失去可观测性。
  • 创建一个额外的特性标志系统 —— 增加维护成本和复杂度。

以下是使用 tokio-rs/tracing 的库中常见的模式:

#[cfg_attr(feature = "tracing", tracing::instrument(skip(password), fields(user_id = user.id)))]
async fn authenticate(user: &User, password: &str) -> Result<AuthToken, AuthError> {
    // ...更多代码...
}

不同的库可能会定义稍有差异的特性名称,这使得最终的应用程序在配置这些标志时变得十分复杂。

对于 tokio-rs/tracing 来说,目前并没有一种干净的方式来实现“零成本的禁用”(zero-cost disabled)。这导致库的作者不愿意在性能敏感的代码路径中添加检测逻辑。

3. 不支持上下文传播

分布式追踪要求在服务边界之间传播上下文信息,但 tokio-rs/tracing 大部分情况下将这个任务留给了开发者来手动处理。例如,下面是 tonic 官方提供的 gRPC 服务追踪示例:

Server::builder()
    .trace_fn(|_| tracing::info_span!("grpc_server"))
    .add_service(MyServiceServer::new(MyService::default()))
    .serve(addr)
    .await?;

上述示例仅仅创建了一个基础的 span,但是并没有从传入的请求中提取追踪上下文。

在分布式系统中,缺乏上下文传播会导致严重的后果。当由于上下文缺失而导致追踪断开时,你将无法看到完整的请求流,例如:

期望的完整追踪流:

Trace #1: 前端 → API 网关 → 用户服务 → 数据库 → 响应

实际看到的却是断开的片段:

Trace #1: 前端 → API 网关  
Trace #2: 用户服务 → 数据库  
Trace #3: API 网关 → 响应  

更糟糕的是,当多个请求交错执行时,这些追踪片段会变得混乱:

Trace #1: 前端 → API 网关  
Trace #2: 前端 → API 网关  
Trace #3: 前端 → API 网关  
Trace #4: 用户服务 → 数据库  
Trace #6: API 网关 → 响应  
Trace #5: 用户服务 → 数据库  

这种碎片化会极大地增加跟踪请求流、隔离性能问题以及理解服务之间因果关系的难度,影响调试和优化的效率。

引入 fastrace:一个快速而完整的解决方案

1. 零成本抽象(Zero-cost Abstraction)

fastrace 设计时采用了真正的零成本抽象。当禁用追踪时,所有的追踪代码会在编译期间被完全移除,因此不会产生任何运行时开销。这使得它非常适合对性能要求敏感的库使用。

2. 生态系统兼容性(Ecosystem Compatibility)

fastrace 专注于分布式追踪,并通过可组合的设计与现有的 Rust 生态系统无缝集成,包括对标准 log crate 的支持。这种架构设计允许库实现全面的追踪功能,同时保留用户选择其偏好的日志设置的自由。

3. 简洁优先(Simplicity First)

API 设计直观且简洁,减少了模板代码的编写,专注于最常见的使用场景,同时在需要时提供扩展能力。

4. 极致性能(Insanely Fast)

fastrace 为高性能应用而生,能够处理大量的 span(追踪片段),并且对 CPU 和内存的使用影响极小。

5. 应用与库的双重适配(Ergonomic for both Applications and Libraries)

fastrace 可以在不引入性能开销的情况下被库使用:

#[fastrace::trace]  // 当未启用 "enable" 特性时,是真正零成本的
pub fn process_data(data: &[u8]) -> Result<Vec<u8>, Error> {
    // 库内部使用标准 log crate
    log::debug!("Processing {} bytes of data", data.len());

    // ...更多代码...
}

关键在于库引入 fastrace 时,不需要开启任何特性:

[dependencies]
fastrace = "0.7"  # 不启用 "enable" 特性

当应用程序使用该库且没有启用 fastrace 的 “enable” 特性时:

  • 所有追踪代码在编译时会被完全优化掉
  • 不会引入任何运行时开销
  • 对性能关键路径没有任何影响

而当应用程序启用了 enable 特性时:

  • 库中的检测逻辑会被激活
  • span 会被收集并上报
  • 应用能够对库的内部行为进行全面可视化

这种设计相较于传统追踪解决方案有显著优势,传统方案通常会始终引入开销,或者要求库作者实现复杂的特性标志系统。

6. 无缝的上下文传播(Seamless Context Propagation)

fastrace 提供了多种主流框架的集成库,能够自动处理上下文传播:

  • HTTP 客户端(reqwest)
let response = client.get(&format!("https://user-service/users/{}", user_id))
    .headers(fastrace_reqwest::traceparent_headers())  // 自动注入追踪上下文
    .send()
    .await?;
  • gRPC 服务端(tonic)
Server::builder()
    .layer(fastrace_tonic::FastraceServerLayer)  // 自动从请求中提取上下文
    .add_service(MyServiceServer::new(MyService::default()))
    .serve(addr);
  • gRPC 客户端
let channel = ServiceBuilder::new()
    .layer(fastrace_tonic::FastraceClientLayer)  // 自动注入上下文到请求中
    .service(channel);
  • 数据访问(Apache OpenDAL)
let op = Operator::new(services::Memory::default())?
    .layer(opendal::layers::FastraceLayer)  // 自动追踪所有数据操作
    .finish();
op.write("test", "0".repeat(16 * 1024 * 1024).into_bytes())
    .await?;

通过这些集成,fastrace 实现了开箱即用的分布式追踪,无需手动处理上下文传播,大幅简化了开发者的工作量。

完整的解决方案:fastrace + log + logforth

fastrace 专注于做好一件事:分布式追踪。通过它的可组合设计以及对 Rust 生态的良好支持,与以下工具共同构建了一个强大的可观测性解决方案:

  • log:Rust 的标准日志接口,用于基础日志记录。
  • logforth:具有工业级特性的灵活日志实现,支持更复杂的日志管理与调度。
  • fastrace:高性能的分布式追踪,支持上下文传播和跨服务链路跟踪。

这种集成可以让日志自动关联到追踪 span,不需要额外切换不同的日志宏:

log::info!("Processing started");

在你的日志基础设施中,你可以清楚地看到每个日志条目对应的追踪 ID 和 span,便于更高效的关联和分析。

完整示例:构建一个具备完整可观测性的微服务

以下是一个基于 fastrace、log 和 logforth 的简洁微服务示例:

#[poem::handler]
#[fastrace::trace]  // 自动创建并管理 span
async fn get_user(Path(user_id): Path<String>) -> Json<User> {
    // 标准日志会自动关联到当前 span
    log::info!("Fetching user {}", user_id);
    let user_details = fetch_user_details(&user_id).await;
    Json(User {
        id: user_id,
        name: user_details.name,
        email: user_details.email,
    })
}

子任务的追踪:

#[fastrace::trace]
async fn fetch_user_details(user_id: &str) -> UserDetails {
    let client = reqwest::Client::new();
    let response = client.get(&format!("https://user-details-service/users/{}", user_id))
        .headers(fastrace_reqwest::traceparent_headers())  // 自动传播追踪上下文
        .send()
        .await
        .expect("Request failed");
    response.json::<UserDetails>().await.expect("Failed to parse JSON")
}

主服务的配置和启动:

#[tokio::main]
async fn main() {
    // 配置日志和追踪
    setup_observability("user-service");
    let app = poem::Route::new()
        .at("/users/:id", poem::get(get_user))
        .with(fastrace_poem::FastraceMiddleware); // 自动提取追踪上下文
    poem::Server::new(poem::listener::TcpListener::bind("0.0.0.0:3000"))
        .run(app)
        .await
        .unwrap();
    fastrace::flush();
}

日志与追踪的初始化:

fn setup_observability(service_name: &str) {
    // 配置 logforth 进行日志管理
    logforth::stderr()
        .dispatch(|d| {
            d.filter(log::LevelFilter::Info)
                // 将追踪 ID 附加到日志
                .diagnostic(logforth::diagnostic::FastraceDiagnostic::default())
                // 将日志附加到 span
                .append(logforth::append::FastraceEvent::default())
        })
        .apply();
    // 配置 fastrace 进行分布式追踪
    fastrace::set_reporter(
        fastrace_jaeger::JaegerReporter::new("127.0.0.1:6831".parse().unwrap(), service_name).unwrap(),
        fastrace::collector::Config::default()
    );
}

总结

fastrace 代表了 Rust 中分布式追踪的现代化解决方案,主要具备以下显著优势:

  • 零运行时开销(Zero Runtime Overhead When Disabled):
  • 当应用未启用追踪时,库中的检测代码会被完全优化掉,不会影响性能。
  • 无生态锁定(No Ecosystem Lock-In):
  • 使用 fastrace 不会强制用户依赖某一特定日志系统,可灵活适配 log、logforth 等多种实现。
  • 简单的 API 接口(Simple API Surface):
  • 简洁的 API 设计,让开发者能够轻松实现全面追踪,而无需复杂的配置。
  • 可预测的性能表现(Predictable Performance):
  • 即使在高负载下,fastrace 的性能依旧稳定、可预测。

如果生态中的库都能全面支持 fastrace,那么应用程序将拥有前所未有的可观测性,而不必担心性能损耗或兼容性问题。

相关资源

  • https://github.com/fast/fastrace
  • https://crates.io/crates/fastrace-jaeger
  • https://crates.io/crates/fastrace-opentelemetry
  • https://crates.io/crates/fastrace-reqwest
  • https://crates.io/crates/fastrace-poem
  • https://crates.io/crates/fastrace-tonic
  • https://crates.io/crates/logforth

这一整套生态的组合,能够让你快速搭建高性能、易扩展、且可全面观测的分布式系统。


观测云的思考

性能对比:零成本抽象带来的优势

与传统的 tokio-rs/tracing 相比,Fastrace 的零成本抽象(Zero-cost Abstraction)设计在未启用时完全移除追踪代码,不会对运行时产生任何性能开销。而 tokio-rs/tracing 即使在未采集数据的情况下,仍会有一定的性能损耗。

此外,Fastrace 的上下文传播是自动化且无缝的,而 tokio-rs/tracing 则需要手动处理上下文,增加了复杂度和潜在的错误风险。

一站式解决方案:Fastrace + 观测云

Fastrace 的强大分布式追踪能力不仅能帮助开发者高效追踪微服务调用链,还能够无缝对接到观测云平台,实现更加全面的可观测性监控。通过将 Fastrace 的 OpenTelemetry 数据直接接入观测云,开发者可以在统一的平台上实时查看链路追踪、性能瓶颈以及跨服务的调用关系,大幅提升问题排查和性能优化的效率。无论是调试复杂的微服务系统,还是在生产环境中快速定位故障,该组合都能以较低的接入成本带来卓越的性能监控体验,真正实现“全链路可观测,一站式可视化”。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2385616.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Linux云计算训练营笔记day13【CentOS 7 find、vim、vimdiff、ping、wget、curl、RPM、YUM】

Linux云计算训练营笔记day13[CentOS 7 find、vim、vimdiff、ping、wget、curl、RPM、YUM]] 目录 Linux云计算训练营笔记day13[CentOS 7 find、vim、vimdiff、ping、wget、curl、RPM、YUM]]1.find练习2.vim高级使用2.1 命令模式:2.2 插入模式:2.3 末行模式: 3. vimdiff4. ping5.…

黑马Java基础笔记-15

Set 无索引&#xff0c;无序&#xff0c;不可重复 HashSet object类中默认hashCode的方法是根据地址值。 如果集合中存储的是自定义对象&#xff0c;必须要重写hashCode和equals方法。 底层原理 jdk8以前&#xff1a;数组 链表 jdk8及以后&#xff1a;数组 链表 红黑…

软件设计师“排序算法”真题考点分析——求三连

一、考点分值占比与趋势分析 综合知识题分值统计表 年份考题数量总分值分值占比考察重点2018222.67%时间复杂度/稳定性判断2019334.00%算法特性对比分析2020222.67%空间复杂度要求2021111.33%算法稳定性判断2022334.00%综合特性应用2023222.67%时间复杂度计算2024222.67%分治…

Visual Studio 2019/2022:当前不会命中断点,还没有为该文档加载任何符号。

1、打开调试的模块窗口&#xff0c;该窗口一定要在调试状态下才会显示。 vs2019打开调试的模块窗口 2、Visual Studio 2019提示未使用调试信息生成二进制文件 未使用调试信息生成二进制文件 3、然后到debug目录下看下确实未生成CoreCms.Net.Web.WebApi.pdb文件。 那下面的…

vue--ofd/pdf预览实现

背景 实现预览ofd/pdf超链接功能 业务实现 pdf的预览 实现方式&#xff1a; 直接使用 <iframe :src"${url}#navpanes0&toolbar0" /> 实现pdf的预览。 navpanes0 隐藏侧边栏toolbar0 隐藏顶部工具栏 使用pdf.js&#xff0c;代码先行&#xff1a; <tem…

Python 爬虫之requests 模块的应用

requests 是用 python 语言编写的一个开源的HTTP库&#xff0c;可以通过 requests 库编写 python 代码发送网络请求&#xff0c;其简单易用&#xff0c;是编写爬虫程序时必知必会的一个模块。 requests 模块的作用 发送网络请求&#xff0c;获取响应数据。 中文文档&#xf…

【MySQL】CRUD

CRUD 简介 CRUD是对数据库中的记录进行基本的增删改查操作 Create&#xff08;创建&#xff09;Retrieve&#xff08;读取&#xff09;Update&#xff08;更新&#xff09;Delete&#xff08;删除&#xff09; 一、新增&#xff08;Create&#xff09; 语法&#xff1a; I…

Spring Boot微服务架构(三):Spring Initializr创建CRM项目

使用Spring Initializr创建CRM项目 一、创建项目前的准备 访问Spring Initializr网站&#xff1a; 打开浏览器访问 https://start.spring.io/或者直接使用IDE&#xff08;如IntelliJ IDEA或Eclipse&#xff09;内置的Spring Initializr功能 项目基本信息配置&#xff1a; Proj…

【笔记】PyCharm 中创建Poetry解释器

#工作记录 在使用 PyCharm 进行 Python 项目开发时&#xff0c;为项目配置合适的 Python 解释器至关重要。Poetry 作为一款强大的依赖管理和打包工具&#xff0c;能帮助我们更便捷地管理项目的依赖项与虚拟环境。下面将详细记录在 PyCharm 中创建 Poetry 解释器的步骤。 前提条…

python中的numpy(数组)

&#xff08;0&#xff09;numpy介绍 NumPy是Python中用于科学计算的基础库&#xff0c;提供高效的多维数组对象ndarray&#xff0c;支持向量化运算&#xff0c;能大幅提高数值计算效率。它集成了大量数学函数&#xff08;如线性代数、傅里叶变换等&#xff09;&#xff0c;可…

rce命令执行原理及靶场实战(详细)

2. 原理 在根源上应用系统从设计上要给用户提供一个指定的远程命令操作的接口。漏洞主要出现在常见的路由器、防火墙、入侵检测等设备的web管理界面上。在管理界面提供了一个ping服务。提交后&#xff0c;系统对该IP进行ping&#xff0c;并且返回结果。如果后台服务器并没有对…

Fuzz 模糊测试篇JS 算法口令隐藏参数盲 Payload未知文件目录

1 、 Fuzz 是一种基于黑盒的自动化软件模糊测试技术 , 简单的说一种懒惰且暴力的技术融合了常见 的以及精心构建的数据文本进行网站、软件安全性测试。 2 、 Fuzz 的核心思想 : 口令 Fuzz( 弱口令 ) 目录 Fuzz( 漏洞点 ) 参数 Fuzz( 利用参数 ) PayloadFuzz(Bypass)…

展示了一个三轴(X, Y, Z)坐标系!

等轴测投影”&#xff08;isometric projection&#xff09;风格的手绘风格三维图&#xff0c;即三条坐标轴&#xff08;x₁, x₂, x₃&#xff09;看起来彼此垂直、等角分布&#xff08;通常是 120 夹角&#xff09;&#xff0c;它是常见于教材和数学书籍的 “假三维”表示法。…

【b站计算机拓荒者】【2025】微信小程序开发教程 - chapter1 初识小程序 - 3项目目录结构4快速上手

3 项目目录结构 3.1 项目目录结构 3.1.1 目录介绍 # 1 项目主配置文件&#xff0c;在项目根路径下&#xff0c;控制整个项目的-app.js # 小程序入口文件&#xff0c;小程序启动&#xff0c;会执行此js-app.json # 小程序全局配置文件&#xff0c;配置小程序导航栏颜色等信息…

LLM Tuning

Lora-Tuning 什么是Lora微调&#xff1f; LoRA&#xff08;Low-Rank Adaptation&#xff09; 是一种参数高效微调方法&#xff08;PEFT, Parameter-Efficient Fine-Tuning&#xff09;&#xff0c;它通过引入低秩矩阵到预训练模型的权重变换中&#xff0c;实现无需大规模修改…

云计算与大数据进阶 | 28、存储系统如何突破容量天花板?可扩展架构的核心技术与实践—— 分布式、弹性扩展、高可用的底层逻辑(下)

在上篇中&#xff0c;我们围绕存储系统可扩展架构详细探讨了基础技术原理与典型实践。然而&#xff0c;在实际应用场景中&#xff0c;存储系统面临的挑战远不止于此。随着数据规模呈指数级增长&#xff0c;业务需求日益复杂多变&#xff0c;存储系统还需不断优化升级&#xff0…

水利数据采集MCU水资源的智能守护者

水利数据采集仪MCU&#xff0c;堪称水资源的智能守护者&#xff0c;其重要性不言而喻。在水利工程建设和水资源管理领域&#xff0c;MCU数据采集仪扮演着不可或缺的角色。它通过高精度的传感器和先进的微控制器技术&#xff0c;实时监测和采集水流量、水位、水质等关键数据&…

origin绘图之【如何将横坐标/x设置为文字、字母形式】

在使用 Origin 进行科研绘图或数据可视化的过程中&#xff0c;我们常常会遇到这样一种需求&#xff1a;希望将横坐标&#xff08;X轴&#xff09;由默认的数字形式&#xff0c;改为字母&#xff08;如 A、B、C……&#xff09;或中文文字&#xff08;如 一、二、三……&#xf…

工业智能网关建立烤漆设备故障预警及远程诊断系统

一、项目背景 烤漆房是汽车、机械、家具等工业领域广泛应用的设备&#xff0c;主要用于产品的表面涂装。传统的烤漆房控制柜采用本地控制方式&#xff0c;操作人员需在现场进行参数设置和设备控制&#xff0c;且存在设备智能化程度低、数据孤岛、设备维护成本高以及依靠传统人…

Kafka Streams 和 Apache Flink 的无状态流处理与有状态流处理

Kafka Streams 和 Apache Flink 与数据库和数据湖相比的无状态和有状态流处理的概念和优势。 在数据驱动的应用中&#xff0c;流处理的兴起改变了我们处理和操作数据的方式。虽然传统数据库、数据湖和数据仓库对于许多基于批处理的用例来说非常有效&#xff0c;但在要求低延迟…