结账服务

该服务负责处理用户的结账订单。结账服务将调用许多其他服务来处理订单。

结账服务源代码

追踪

初始化追踪

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

func initTracerProvider() *sdktrace.TracerProvider {
    ctx := context.Background()

    exporter, err := otlptracegrpc.New(ctx)
    if err != nil {
        log.Fatal(err)
    }
    tp := sdktrace.NewTracerProvider(
        sdktrace.WithBatcher(exporter),
        sdktrace.WithResource(initResource()),
    )
    otel.SetTracerProvider(tp)
    otel.SetTextMapPropagator(propagation.NewCompositeTextMapPropagator(propagation.TraceContext{}, propagation.Baggage{}))
    return tp
}

在服务关闭时,您应调用TracerProvider.Shutdown()以确保所有跨度都被导出。该服务在主函数中使用延迟函数进行此调用。

tp := initTracerProvider()
defer func() {
    if err := tp.Shutdown(context.Background()); err != nil {
        log.Printf("关闭跟踪器提供程序出错:%v", err)
    }
}()

添加 gRPC 自动仪表化

该服务接收 gRPC 请求,这些请求在主函数中作为 gRPC 服务器创建的一部分进行仪表化。

var srv = grpc.NewServer(
    grpc.UnaryInterceptor(otelgrpc.UnaryServerInterceptor()),
    grpc.StreamInterceptor(otelgrpc.StreamServerInterceptor()),
)

该服务将发起多个出站 gRPC 调用,这些调用都通过包含仪表化的 gRPC 客户端进行仪表化。

func createClient(ctx context.Context, svcAddr string) (*grpc.ClientConn, error) {
    return grpc.DialContext(ctx, svcAddr,
        grpc.WithTransportCredentials(insecure.NewCredentials()),
        grpc.WithUnaryInterceptor(otelgrpc.UnaryClientInterceptor()),
        grpc.WithStreamInterceptor(otelgrpc.StreamClientInterceptor()),
    )
}

添加Kafka(Sarama)自动仪表化

该服务将处理结果写入一个 Kafka 主题,然后由其他微服务处理。为了仪表化 Kafka 客户端,必须在创建 Producer 后对其进行封装。

saramaConfig := sarama.NewConfig()
producer, err := sarama.NewAsyncProducer(brokers, saramaConfig)
if err != nil {
    return nil, err
}
producer = otelsarama.WrapAsyncProducer(saramaConfig, producer)

添加属性以进行自动仪表化的跨度

在自动仪表化的代码执行过程中,您可以从上下文中获取当前跨度。

span := trace.SpanFromContext(ctx)

使用跨度对象上的SetAttributes方法可以为跨度添加属性。在PlaceOrder函数中,将为跨度添加多个属性。

span.SetAttributes(
    attribute.String("app.order.id", orderID.String()), shippingTrackingAttribute,
    attribute.Float64("app.shipping.amount", shippingCostFloat),
    attribute.Float64("app.order.amount", totalPriceFloat),
    attribute.Int("app.order.items.count", len(prep.orderItems)),
)

添加跨度事件

使用跨度对象上的AddEvent方法可以添加跨度事件。在PlaceOrder函数中,添加了多个跨度事件。其中一些事件具有附加属性,其他事件没有。

添加没有属性的跨度事件:

span.AddEvent("prepared")

添加具有附加属性的跨度事件:

span.AddEvent("charged",
    trace.WithAttributes(attribute.String("app.payment.transaction.id", txID)))

指标

初始化指标

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

func initMeterProvider() *sdkmetric.MeterProvider {
    ctx := context.Background()

    exporter, err := otlpmetricgrpc.New(ctx)
    if err != nil {
        log.Fatalf("初始化 otlp 指标 gRPC 导出器失败:%v", err)
    }

    mp := sdkmetric.NewMeterProvider(sdkmetric.WithReader(sdkmetric.NewPeriodicReader(exporter)))
    global.SetMeterProvider(mp)
    return mp
}

在服务关闭时,您应调用MeterProvider.Shutdown()以确保所有记录都被导出。该服务在主函数中使用延迟函数进行此调用。

mp := initMeterProvider()
defer func() {
    if err := mp.Shutdown(context.Background()); err != nil {
        log.Printf("关闭计量器提供程序出错:%v", err)
    }
}()

添加 Golang 运行时自动仪表化

在主函数中对 Golang 运行时进行仪表化。

err := runtime.Start(runtime.WithMinimumReadMemStatsInterval(time.Second))
if err != nil {
    log.Fatal(err)
}

日志

待定

最后修改 December 13, 2023: improve glossary translation (46f8201b)