Rust请求追踪库tower-request-id的使用,为HTTP请求添加唯一标识符实现分布式追踪

Rust请求追踪库tower-request-id的使用,为HTTP请求添加唯一标识符实现分布式追踪

tower-request-id是一个用于tower生态(hyper, axum, warp等)的小型服务,为每个传入请求生成随机ID。

使用示例

RequestIdLayer添加到axum中间件后,请求ID可以通过http::Request::extensions()获取。对于tracing集成,请参考日志记录示例。

完整示例代码

use axum::{
    routing::get,
    Router,
    extract::Request,
    middleware::{self, Next},
    response::IntoResponse,
};
use tower_request_id::{RequestId, RequestIdLayer};
use uuid::Uuid;

async fn handler(request: Request) -> impl IntoResponse {
    // 从请求扩展中获取请求ID
    let request_id = request.extensions().get::<RequestId>().unwrap();
    format!("Request ID: {}", request_id)
}

async fn log_request_id(request: Request, next: Next) -> impl IntoResponse {
    // 在中间件中获取请求ID
    let request_id = request.extensions().get::<RequestId>().unwrap();
    println!("Request ID in middleware: {}", request_id);
    
    next.run(request).await
}

#[tokio::main]
async fn main() {
    let app = Router::new()
        .route("/", get(handler))
        // 添加请求ID层
        .layer(RequestIdLayer::with_generator(|| Uuid::new_v4().to_string()))
        // 添加记录请求ID的中间件
        .layer(middleware::from_fn(log_request_id));

    axum::Server::bind(&"0.0.0.0:3000".parse().unwrap())
        .serve(app.into_make_service())
        .await
        .unwrap();
}

代码说明

  1. 首先我们导入必要的模块和类型
  2. 创建一个简单的handler函数,从中获取并返回请求ID
  3. 创建一个中间件函数log_request_id,演示如何在中间件中访问请求ID
  4. 在main函数中:
    • 创建路由
    • 添加RequestIdLayer中间件层,使用UUID作为ID生成器
    • 添加我们的日志记录中间件
    • 启动服务器

这个示例展示了如何:

  • 为每个请求自动生成唯一ID
  • 在handler和中间件中访问这些ID
  • 使用自定义ID生成器(这里使用UUID)

许可证

本项目使用MIT许可证。


1 回复

Rust请求追踪库tower-request-id的使用:为HTTP请求添加唯一标识符实现分布式追踪

介绍

tower-request-id是一个Rust中间件库,用于为HTTP请求添加唯一标识符(Request ID),帮助实现分布式系统追踪。它基于tower生态构建,可以轻松集成到使用toweraxum等框架的应用中。

主要功能:

  • 为每个HTTP请求生成唯一ID
  • 支持自定义ID生成策略
  • 可以从请求头中提取已有ID
  • 将ID注入到请求和响应中

完整示例代码

下面是一个完整的示例,展示了如何使用tower-request-id为HTTP请求添加唯一标识符:

use axum::{
    routing::get,
    Router,
    extract::Request,
    middleware,
    response::Response,
    body::Body
};
use tower::ServiceBuilder;
use tower_request_id::{RequestId, RequestIdLayer};
use uuid::Uuid;
use tracing::info;
use tower_http::trace::TraceLayer;

// 处理函数,显示请求ID
async fn handler(request_id: RequestId) -> String {
    info!(request_id = %request_id, "处理请求");
    format!("请求ID: {}", request_id)
}

// 自定义中间件,记录请求ID
async fn request_id_middleware(request: Request, next: middleware::Next) -> Response {
    let request_id = request.extensions().get::<RequestId>().unwrap().clone();
    info!(%request_id, "开始处理请求");
    
    let response = next.run(request).await;
    
    info!(%request_id, "请求处理完成");
    response
}

#[tokio::main]
async fn main() {
    // 初始化tracing日志
    tracing_subscriber::fmt().init();

    let app = Router::new()
        .route("/", get(handler))
        .layer(
            ServiceBuilder::new()
                // 添加请求ID层,使用UUID v4生成ID
                .layer(RequestIdLayer::new(
                    || Uuid::new_v4().to_string(),
                    "x-request-id".to_string(),
                ))
                // 添加HTTP追踪层
                .layer(TraceLayer::new_for_http())
                // 添加自定义中间件层
                .layer(middleware::from_fn(request_id_middleware))
        );

    let addr = "0.0.0.0:3000".parse().unwrap();
    info!("服务器启动,监听 {}", addr);
    
    axum::Server::bind(&addr)
        .serve(app.into_make_service())
        .await
        .unwrap();
}

代码说明

  1. 依赖配置 (Cargo.toml):
[dependencies]
axum = "0.7"
tower = "0.4"
tower-request-id = "0.3"
tower-http = "0.4"
uuid = { version = "1.3", features = ["v4"] }
tracing = "0.1"
tracing-subscriber = "0.3"
tokio = { version = "1.0", features = ["full"] }
  1. 核心组件:
  • RequestIdLayer: 生成请求ID的中间件层
  • RequestId: 请求ID类型,可以从请求扩展中获取
  • TraceLayer: HTTP请求追踪中间件
  • tracing: 日志记录框架
  1. 工作流程:
  • 每个请求到达时,RequestIdLayer会生成唯一ID
  • 中间件和处理器可以通过请求扩展获取这个ID
  • ID会被自动添加到响应头中
  • 所有日志都会包含请求ID,便于追踪

最佳实践

  1. 一致性: 建议使用标准的请求头名称如x-request-id
  2. 传播: 调用下游服务时,确保传递请求ID
  3. 日志: 在关键日志点记录请求ID
  4. 格式: 使用UUID或ULID等标准格式,确保全局唯一性

这个完整示例展示了如何在实际项目中使用tower-request-id实现请求追踪,包括ID生成、日志记录和中间件集成。

回到顶部