物流服务

此服务负责在从结账服务请求时提供物流信息,包括价格和追踪信息。

物流服务主要使用 Tonic、Reqwest 和 OpenTelemetry 库/组件构建。其他子依赖项包含在 Cargo.toml 中。

根据您的框架和运行时,您可能需要参考Rust文档以作为补充。您可以在报价请求中找到异步和同步跨度的示例,以及跟踪 ID。

build.rs 支持在无 Docker 的情况下进行开发,只需安装 Rust。否则,考虑使用 docker compose 进行构建以编辑/评估所需的更改。

物流服务源代码

链路追踪

初始化追踪

OpenTelemetry SDK 在 main 函数中进行初始化。

fn init_tracer() -> Result<sdktrace::Tracer, TraceError> {
    global::set_text_map_propagator(TraceContextPropagator::new());
    let os_resource = OsResourceDetector.detect(Duration::from_secs(0));
    let process_resource = ProcessResourceDetector.detect(Duration::from_secs(0));
    let sdk_resource = SdkProvidedResourceDetector.detect(Duration::from_secs(0));
    let env_resource = EnvResourceDetector::new().detect(Duration::from_secs(0));
    let telemetry_resource = TelemetryResourceDetector.detect(Duration::from_secs(0));
    opentelemetry_otlp::new_pipeline()
        .tracing()
        .with_exporter(
            opentelemetry_otlp::new_exporter()
                .tonic()
                .with_endpoint(format!(
                    "{}{}",
                    env::var("OTEL_EXPORTER_OTLP_TRACES_ENDPOINT")
                        .unwrap_or_else(|_| "http://otelcol:4317".to_string()),
                    "/v1/traces"
                )), // TODO: assume this ^ is true from config when opentelemetry crate > v0.17.0
                    // https://github.com/open-telemetry/opentelemetry-rust/pull/806 includes the environment variable.
        )
        .with_trace_config(
            sdktrace::config()
                .with_resource(os_resource.merge(&process_resource).merge(&sdk_resource).merge(&env_resource).merge(&telemetry_resource)),
        )
        .install_batch(opentelemetry::runtime::Tokio)
}

此示例中,我们创建了一些资源探测器并配置了追踪导出器。这段代码将初始化 OpenTelemetry SDK,并设置了采样器和追踪配置。可以参考 Rust 文档了解更多详细信息。

添加 gRPC 仪表板

此服务接收 gRPC 请求,并在中间件中进行仪表板记录。

在同一线程中启动根跨度,并将其引用传递给另一个闭包,在闭包中调用 quoteservice

    let tracer = global::tracer("shippingservice");
    let mut span = tracer.span_builder("oteldemo.ShippingService/GetQuote").with_kind(SpanKind::Server).start_with_context(&tracer, &parent_cx);
    span.set_attribute(semcov::trace::RPC_SYSTEM.string(RPC_SYSTEM_GRPC));

    span.add_event("Processing get quote request".to_string(), vec![]);

    let cx = Context::current_with_span(span);
    let q = match create_quote_from_count(itemct)
        .with_context(cx.clone())
        .await
//-> create_quote_from_count()...
    let f = match request_quote(count).await {
        Ok(float) => float,
        Err(err) => {
            let msg = format!("{}", err);
            return Err(tonic::Status::unknown(msg));
        }
    };

    Ok(get_active_span(|span| {
        let q = create_quote_from_float(f);
        span.add_event(
            "Received Quote".to_string(),
            vec![KeyValue::new("app.shipping.cost.total", format!("{}", q))],
        );
        span.set_attribute(KeyValue::new("app.shipping.items.count", count as i64));
        span.set_attribute(KeyValue::new("app.shipping.cost.total", format!("{}", q)));
        q
    }))
//<- create_quote_from_count()...
    cx.span().set_attribute(semcov::trace::RPC_GRPC_STATUS_CODE.i64(RPC_GRPC_STATUS_CODE_OK));

注意我们在根跨度周围创建了一个上下文,并将其克隆到异步函数 create_quote_from_count() 中。在 create_quote_from_count() 完成后,我们可以根据需要向根跨度添加其他属性。

您还可以注意到此示例中对跨度设置的属性和类似的传播的事件。使用任何有效的跨度指针(附加到上下文)都可以操作OpenTelemetry API

添加 HTTP 仪表板

对于通过 reqwest 客户端发送的出站 HTTP 调用到 quoteservice,也会生成一个子客户端跨度,与相应的 quoteservice 服务器跨度配对。跟踪工具的实施在客户端中间件中完成,使用了可用的 reqwest-middlewarereqwest-tracingtracing-opentelemetry 库。

let reqwest_client = reqwest::Client::new();
let client = ClientBuilder::new(reqwest_client)
    .with(TracingMiddleware::<SpanBackendWithUrl>::new())
    .build();

添加跨度属性

只要您在相同的线程上,或在跨度拥有线程中传递的上下文中,或在作用域中存在 ContextGuard,都可以使用 get_active_span 获取一个活跃的跨度。您可以在演示中找到这些情况的示例,其中 shipping_service 中提供了同步/异步运行时的上下文。您应该查阅 quote.rs 或上面的示例以查看传递给异步运行时的上下文。

以下是 shiporder 中的一个片段,其中保持了上下文和跨度。这在我们的同步运行时中是合适的。

let parent_cx =
global::get_text_map_propagator(|prop| prop.extract(&MetadataMap(request.metadata())));
// 在这个例子中,生成追踪ID非常简单
//我们将会在这个函数中创建一个跨度和相关的事件。
let tracer = global::tracer("shippingservice");
let mut span = tracer
    .span_builder("oteldemo.ShippingService/ShipOrder").with_kind(SpanKind::Server).start_with_context(&tracer, &parent_cx);

您必须在具有上下文的跨度中使用 set_attribute 添加属性,然后使用包含键和值的 KeyValue 对象。

let tid = create_tracking_id();
span.set_attribute(KeyValue::new("app.shipping.tracking.id", tid.clone()));
info!("Tracking ID Created: {}", tid);

添加跨度事件

使用 span 对象的 add_event 方法可以添加跨度事件。服务器路由(同步的 ShipOrderRequest 和异步的 GetQuoteRequest)都有跨度事件。此处没有包含属性,但是属性的添加非常简单。

添加跨度事件:

let tid = create_tracking_id();
span.set_attribute(KeyValue::new("app.shipping.tracking.id", tid.clone()));
info!("Tracking ID Created: {}", tid);

指标

待定

日志

待定

最后修改 December 10, 2023: translate (a4350d6e)