Rust 中的分佈式跟蹤:OpenTelemetry
在這篇文章中,我們先簡單介紹一下 OpenTelemetry,然後,再來編寫一個在 Rust 中使用 OpenTelemetry 的示例。
OpenTelemetry,也被簡稱爲 OTel,是一個開源可觀察性框架,用於檢測、生成、收集和導出測量數據,如跟蹤、度量、日誌。OTEL 爲你提供了一套獨立的 api、sdk、集成和收集器服務。如圖:
下面,讓我們來看一下 OpenTelemetry 的關鍵概念。
OpenTelemetry 介紹
核心術語
跟蹤 (Trace):
-
描述請求所經過的整個路徑
-
軌跡包含跨度 (Span):服務之間的跨度、接口調用之間的跨度等。
-
跨度是軌跡的基石
指標 (Metric):
-
定義爲在運行時捕獲的服務的度量數據。
-
度量數據可以是系統調用的持續時間、CPU 或內存使用情況。
日誌 (Logs):
- 帶時間戳的文本記錄
上下文信息 (Baggage):
-
OpenTelemetry 可以跨多個服務之間的多個跨度傳播上下文信息,比如用戶 ID。你可以將其附加到跟蹤中的每個跨度。
-
使用上下文信息時有一些注意事項,上下文信息存儲在 HTTP 頭中,它將暴露給任何可以檢查你的網絡數據包的人。
OpenTelemetry 的構成
語義約定:爲常見的遙測類型 (如應用程序名稱、HTTP 調用等) 定義標準命名約定。
-
API:定義要生成的遙測、度量、日誌和跟蹤。
-
SDK:OpenTelmetry 的一些 sdk 可以爲通用庫和框架提供自動檢測。
-
OpenTelmetry 協議 (OTLP):用於向可觀察性後端發送數據的協議。
-
跨服務傳播器:傳播器在不同的服務和進程之間傳輸數據。
-
資源探測器:將產生遙測的實體標記爲資源屬性。
OpenTelemetry 數據處理
-
數據收集器:定義收集器如何攝取數據,推或拉。
-
數據處理器:定義在導出數據之前如何處理數據。
-
數據導出器:定義如何將數據發送到可觀察性後端,推或拉。
在 Rust 中使用 OpenTelemetry
在開始檢測應用程序之前,我們需要向一個可觀察性的後端來發送遙測數據,最容易上手的就是 Jaeger。
設置 Jaeger
啓動 Jaeger 的最簡單方法是使用 Docker 鏡像:
docker run -d --name jaeger \
-e COLLECTOR_ZIPKIN_HOST_PORT=:9411 \
-e COLLECTOR_OTLP_ENABLED=true \
-p 6831:6831/udp \
-p 6832:6832/udp \
-p 5778:5778 \
-p 16686:16686 \
-p 4317:4317 \
-p 4318:4318 \
-p 14250:14250 \
-p 14268:14268 \
-p 14269:14269 \
-p 9411:9411 \
jaegertracing/all-in-one:1.47
在這裏 16686 是前端顯示端口;4318 是接收遙測數據的 HTTP 端口,4317 是接收遙測數據的 GRPC 端口,它們都遵循 OTLP 協議。
一旦它啓動並運行,可以在瀏覽器中輸入:http://localhost:16686/ 來觀察數據。
創建項目
在這個例子中,定義一個簡單的函數生成一個隨機數並打印出來,然後在執行時返回一個結果。
使用如下命令創建一個 Rust 項目:
cargo new otlp-example
然後在 Cargo.toml 文件中,加入以下依賴項:
[dependencies]
opentelemetry = { version = "0.19.0", features = ["rt-tokio"] }
opentelemetry-otlp = { version="0.12.0", features = ["tonic", "metrics"] }
opentelemetry-semantic-conventions = { version="0.11.0" }
tokio = { version = "1.0", features = ["full"] }
rand = "0.8.5"
在 src/main.rs 中,寫入如下代碼:
use opentelemetry::trace::TraceError;
use rand::Rng;
fn gen_number() -> u32 {
let mut rng = rand::thread_rng();
rng.gen()
}
#[tokio::main]
async fn main() -> Result<(), TraceError> {
let num = gen_number();
println!("{}", num);
Ok(())
}
創建 init_tracer 函數來初始化 OpenTelemetry 跟蹤器:
fn init_tracer() -> Result<trace::Tracer, TraceError> {
// 初始化OTLP管道
opentelemetry_otlp::new_pipeline()
.tracing() // 創建OTLP跟蹤管道
.with_exporter(
opentelemetry_otlp::new_exporter()
.tonic() // 創建GRPC層
.with_endpoint("http://localhost:4317"), // GRPC OTLP Jaeger端口
)
// 跟蹤配置
.with_trace_config(
trace::config().with_resource(Resource::new(vec![KeyValue::new(
opentelemetry_semantic_conventions::resource::SERVICE_NAME,
"rust-otlp-basic",
)])),
)
.install_batch(runtime::Tokio) // 配置一個span導出器
}
在 main 函數中使用跟蹤器:
// 創建一個常量 Key
const RANDOM: Key = Key::from_static_str("random.value");
#[tokio::main]
async fn main() -> Result<(), TraceError> {
// 設置全局傳播器
global::set_text_map_propagator(TraceContextPropagator::new());
// 初始化跟蹤程序
let tracer = init_tracer().unwrap();
// 啓動一個新的活躍的跨度
tracer.in_span("generating number", |cx| {
let span = cx.span();
let num = gen_number();
span.add_event(
"Generating Number".to_string(),
vec![Key::new("number").i64(num.into())],
);
// 設置span屬性
span.set_attribute(RANDOM.i64(10));
// 開始一個新的span
tracer.in_span("generate another number", |cx| {
let span = cx.span();
let num = gen_number();
span.add_event(
"Generating Number".to_string(),
vec![Key::new("number").i64(num.into())],
)
})
});
// 優雅地關閉跟蹤器
global::shutdown_tracer_provider();
Ok(())
}
執行 cargo run 運行程序。
執行程序多次後,打開 Jaeger UI:http://localhost:16686/,選擇我們配置的服務名稱:rust-otlp-basic。如圖:
像這樣使用 OpenTelemetry 還是有些複雜,在下一篇文章中,我們將引入 Tracing crates,來簡化 OpenTelemetry 的使用。
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/OW4vWSR3PQWEoe4Lk5o8qw