Fastrace:Rust 中分布式追蹤的現代化方案

原文鏈接: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 cratelog::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> {// 標準日志會自動關聯到當前 spanlog::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.pswp.cn/web/81088.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/81088.shtml
英文地址,請注明出處:http://en.pswp.cn/web/81088.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

水果系列數據集- 葡萄grapes>> DataBall

該數據集可以用于目標檢測&#xff0c;水果分類 &#xff0c;文生圖相關項目。 以下是圖片樣例&#xff1a;

HTTP協議接口三種測試方法之-postman

HTTP協議作為現代Web開發的基石&#xff0c;其接口測試是開發過程中不可或缺的環節。Postman作為最流行的API測試工具之一&#xff0c;能夠極大提升我們的測試效率。本文將詳細介紹如何使用Postman進行HTTP接口測試。 一、HTTP協議基礎回顧 在開始使用Postman之前&#xff0c…

佰力博科技與您探討半導體電阻測試常用的一些方法

一、兩探針法? 兩探針法是一種較為基礎的測試方法。該方法將兩根探針與半導體樣品表面緊密接觸&#xff0c;通過電源在兩根探針之間施加電壓&#xff0c;同時使用電流表測量通過樣品的電流&#xff0c;再根據歐姆定律計算電阻。?這種方法的優點在于操作簡單、設備要求較低&a…

機器學習的一些基本概念

看了b站一個清華博士的視頻做的筆記&#xff0c;對于人工智能的底層原理&#xff0c;訓練方式&#xff0c;以及生成式文本輸出&#xff0c;圖片生成的底層原理有了一個了解&#xff0c;算是一個還不錯的科普文。之前一直想要了解一下機器學習的入門原理&#xff0c;神經網絡相關…

Python爬蟲實戰:研究Grab 框架相關技術

1. 引言 1.1 研究背景與意義 隨著互聯網的快速發展,網絡上的數據量呈爆炸式增長。如何高效地獲取和利用這些數據成為了當前的研究熱點。網絡爬蟲作為一種自動獲取網頁內容的技術,能夠按照一定的規則,自動地抓取萬維網信息,在搜索引擎、數據挖掘、信息整合等領域有著廣泛的…

uniapp 嵌入鴻蒙原生組件 具體步驟

關于怎么使用uniapp 嵌入鴻蒙原生組件 HBuilder X 版本 4.64 app-harmony文件下新建 index.uts button.ets button.ets里面復制uniapp 官方提供的 示例代碼 https://uniapp.dcloud.net.cn/tutorial/harmony/native-component.html button.ets import { NativeEmbedBuilderO…

阿里云 OS Copilot 使用指南

安裝&#xff1a; AlibabaCloudLinux: sudo yum install -y os-copilotUbuntu&#xff1a; curl -#S https://mirrors.aliyun.com/os-copilot/os-copilot-all-in-one-latest.sh | bash添加RAM用戶 打開 https://ram.console.aliyun.com/users 復制AccessKey&#xff0c;Ac…

枚舉類擴充處理

問題背景 由于 Java 不允許枚舉繼承另一個枚舉&#xff08;enum cannot extend enum&#xff09;&#xff0c;但可以通過 組合方式 或 工具類 來實現類似功能。 ? 解決方案一&#xff1a;組合方式引入原始枚舉值 示例代碼&#xff1a; public enum CustomErrorCodeEnum imp…

Spring Security探索與應用

Spring Security核心概念 框架定位與核心能力 Spring Security是Spring生態中實現應用級安全的核心框架,其官方定義為"強大且高度可定制的認證與訪問控制框架"。作為Spring應用程序安全防護的事實標準解決方案,它通過模塊化設計提供以下核心能力: 認證(Authenti…

藍橋杯國14 不完整的算式

&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;理清思路 然后一步步寫 問題描述 小藍在黑板上寫了一個形如 AopBC 的算式&#x…

掃描電鏡:打開微觀世界的“超維相機“

當你用手機拍攝一朵花的微距照片時&#xff0c;放大100倍已足夠驚艷。但如果告訴你&#xff0c;科學家手中的"相機"能將物體放大百萬倍&#xff0c;連病毒表面的蛋白突觸都清晰可見&#xff0c;你是否會好奇這背后的黑科技&#xff1f;這把打開微觀宇宙的鑰匙&#x…

JVM學習(四)--對象內存布局

目錄 一、對象內存布局 1、對象的實例化 1.1、你有幾種方式創建對象&#xff1f; 1.2、創建對象的步驟 1.2.1、從字節碼角度看待對象創建過程 1.2.2、從執行步驟角度分析 2、對象的內存布局 2.1、對象頭 2.2、實例數據 2.3、對齊填充 3、對象的訪問定位 3.1、句柄訪…

SQL每日一題(4)

前言&#xff1a;第四更 雖然已經全部做完了&#xff0c;這套卷子非常推薦&#xff01; 根據題目還原出來的原始表 employees表 idnameagestatus1張三28在崗2李四35在崗3王五42在崗4趙六NULL在崗5錢七58在崗6孫八24在崗7周九31離職8吳十-5在崗9鄭十一45在崗10王十二52在崗 題…

如何做好一份技術文檔?

文章目錄 前言一、技術文檔的核心原則二、技術文檔的類型與場景三、技術文檔的寫作流程3.1 需求分析階段&#xff08;文檔生產的基礎&#xff09;3.2 架構設計階段&#xff08;文檔的骨架搭建&#xff09;3.3 內容開發階段&#xff08;血肉填充&#xff09;3.4 質量保障階段&am…

怎么判斷一個Android APP使用了KMM這個跨端框架

要判斷一個 Android 應用是否使用了 KMM&#xff08;Kotlin Multiplatform Mobile&#xff09; 框架&#xff0c;可以通過以下方法逐步驗證&#xff1a; 一、安裝包結構分析 解壓 APK 將 .apk 文件重命名為 .zip 并解壓&#xff0c;檢查以下特征&#xff1a; ? kotlin/ 目錄&a…

Axure系統原型設計列表版方案

列表頁面是眾多系統的核心組成部分&#xff0c;承擔著數據呈現與基礎交互的重要任務。一個優秀的列表版設計&#xff0c;能夠極大提升用戶獲取信息的效率&#xff0c;優化操作體驗。下面&#xff0c;我們將結合一系列精心設計的列表版方案圖片&#xff0c;深入探討如何打造出實…

HarmonyOS優化應用內存占用問題性能優化四

一、使用purgeable優化C內存 Purgeable Memory是HarmonyOS中native層常用的內存管理機制&#xff0c;可用于圖像處理的Bitmap、流媒體應用的一次性數據、圖片等。應用可以使用Purgeable Memory存放其內部的緩存數據&#xff0c;并由系統根據淘汰策略統一管理全部的purgeable內存…

第一課如何學習課程

讀薄就這些東西 讀厚細節&#xff1b;實現了還是很混沌&#xff08;內功心法&#xff09;-》有個項目&#xff1b;在聽的過程中寫博客&#xff1a;我學了什么&#xff0c;敲代碼&#xff1b; 第二章 網絡-》9.1kv存儲 第三章 基礎組件 &#xff08;API函數、最基礎的底層架構…

Linux 系統不終止進程的情況下關閉長連接

使用 tcpkill 中斷指定 TCP 連接 適用場景&#xff1a;需主動中斷已知源IP或目標端口的連接&#xff0c;無需進程重啟。 安裝 dsniff 工具&#xff08;包含 tcpkill&#xff09;&#xff1a; yum -y install dsniff 捕獲并殺死特定連接&#xff08;例如目標IP 192.168.1.10…

TCP原理解析

目錄 TCP協議概述 1. 基礎尋址段?? ??2. 序列控制段?? ??3. 控制信息段?? ??4. 流量控制段?? ??5. 校驗與應急段?? ??6. 擴展功能段?? ??7. 數據承載段?? TCP原理 確認應答與序列號(安全機制) 超時重傳機制(安全機制) 連接管理機制&…