跳到主要内容

Go API中分布式追踪的上下文传播

分布式追踪允许您的企业应用程序追踪消息从发布者通过事件网格到接收应用程序的流动。有关详细概述,请参阅分布式追踪。有关版本要求的信息,请参阅分布式追踪版本兼容性。

  • PubSub+ OpenTelemetry API 库仅支持 W3C 传播器。
  • 有关配置 OpenTelemetry SDK 环境变量的信息,请参阅 OpenTelemetry SDK 配置。
  • 默认情况下,追踪包括后端应用程序(如 Jaeger)可见的命令行参数。出于安全原因,重要的是禁用此功能,因为这些参数可能包含敏感信息,如您的用户名和密码。有关说明,请参阅 GitHub 上 OpenTelemetry 文档中的禁用自动资源提供程序。

为分布式追踪对Go进行工具化

手动工具化涉及对您的企业应用程序的源代码进行更改,并允许您将额外的上下文(如行李和追踪状态)注入到消息中。上下文传播使调试和优化您的应用程序变得容易。有关 Solace 事件消息中上下文传播的更多信息,请参阅分布式追踪上下文传播。以下示例展示了如何使用 OpenTelemetry API 创建跨度。

了解Go PubSub+ API中上下文传播如何启用分布式追踪

在您的客户端应用程序中,您可以使用 OpenTelemetry API 创建一个跨度,其中包含分布式系统中操作的元数据。此跨度与一个上下文相关联,该上下文包括一个唯一的 TraceID。接下来,当您使用 PubSub+ 消息生产者发布消息时,Solace OTel 集成包将上下文(包含 TraceID)注入到消息中。当消息通过事件代理并被消费应用程序接收时,每个步骤都会生成跨度,并且这些跨度具有原始消息上下文中存在的相同 TraceID。当发布或消费应用程序中的每个跨度关闭时,Go OpenTelemetry API 将其发送到 OpenTelemetry 收集器,该收集器收集、处理并将跨度导出到使用其唯一 TraceID 相关跨度的后端应用程序。后端应用程序使用相关跨度创建一个 trace,这是一个端到端的快照,详细说明了消息如何通过分布式系统传输。如果您不使用上下文传播,则后端应用程序无法使用唯一的 TraceID 将跨度链接起来,这使得追踪消息通过分布式系统的流动变得困难。

依赖项

要启用分布式追踪的上下文传播,您必须首先将 Solace PubSub+ OpenTelemetry 集成 For Solace Go API 作为依赖项添加到您的应用程序中。您也可以使用以下命令添加此包:

go get solace.dev/go/messaging-trace/opentelemetry

然后使用以下命令添加上下文传播所需的 OpenTelemetry API 和 SDK 库:

go get -u go.opentelemetry.io/otel
go get -u go.opentelemetry.io/otel/sdk

有关 OpenTelemetry 版本兼容性的信息,请参阅分布式追踪版本兼容性。添加这些库使您能够访问 TextMapCarrier 接口,该接口使您的应用程序能够:

  • NewInboundMessageCarrier — 包含 OpenTelemetry API 注入和提取入站消息中的追踪和行李所需的所有函数。
  • NewOutboundMessageCarrier — 包含 OpenTelemetry API 注入和提取出站消息中的追踪和行李所需的所有函数。

本指南假设您熟悉配置 OpenTelemetry 类的实例。有关配置 OpenTelemetry 对象的说明,请参阅 OpenTelemetry 文档中的 Go 中的 OpenTelemetry 手动工具化。

要使用 Go PubSub+ API 中的上下文传播,请在您的应用程序中包含以下包:

import (
// ...
// ...
"go.opentelemetry.io/otel/attribute" // 处理 OpenTelemetry 属性的包
semconv "go.opentelemetry.io/otel/semconv/v1.19.0" // OpenTelemetry 的语义约定
"go.opentelemetry.io/otel/trace" // 与 OpenTelemetry 追踪一起工作的包
"go.opentelemetry.io/otel/baggage" // (可选)与 OpenTelemetry 行李一起工作的包
"go.opentelemetry.io/otel" // OpenTelemetry 核心库,用于工具化和 API
"go.opentelemetry.io/otel/exporters/stdout/stdouttrace" // 控制台追踪导出器的实现
sdktrace "go.opentelemetry.io/otel/sdk/trace" // 访问 OpenTelemetry SDK 提供的追踪功能
otel_propagation "go.opentelemetry.io/otel/propagation" // OpenTelemetry 上下文传播的包。使用别名以防止冲突。
propagation "solace.dev/go/messaging-trace/opentelemetry" // Solace PubSub+ Otel 集成
)

在消息发布时生成发送跨度

您的发布应用程序可以生成发送跨度并将其导出到 OpenTelemetry 收集器。以下步骤展示了如何将上下文注入消息并为发布的消息生成发送跨度:

  1. 初始化并创建一个导出器,它允许您将数据导出到 OpenTelemetry 后端。然后,设置一个 traceProvider,它管理您的应用程序中追踪器的生命周期。在您的应用程序能够开始接收有效的 spanContext 数据之前,需要初始化 traceProvider
exporter, _ := stdouttrace.New(stdouttrace.WithPrettyPrint())
batchspanProcessor := sdktrace.NewSimpleSpanProcessor(exporter) // 在生产环境中,您应该使用批量跨度处理器
traceProvider := sdktrace.NewTracerProvider(
sdktrace.WithSampler(sdktrace.AlwaysSample()),
sdktrace.WithSpanProcessor(batchspanProcessor),
)
  1. 初始化并创建 TraceContext 和可选的 Baggage 传播器实例,并使用 SetTextMapPropagator() 函数将它们注册。此函数使 TraceContextBaggage 结构在您的应用程序中可用。使用 SetTracerProvider() 函数注册您的全局追踪器提供程序,这确保了您代码中创建的所有追踪器都使用 traceProvider 中指定的设置:
otel.SetTextMapPropagator(otel_propagation.TraceContext{})
otel.SetTracerProvider(traceProvider)

// 如果您使用行李传播:
otel.SetTextMapPropagator(propagation.NewCompositeTextMapPropagator(otel_propagation.TraceContext{}, otel_propagation.Baggage{}))
otel.SetTracerProvider(traceProvider)
  1. 创建一个 NewOutboundMessageCarrier 实例,这是一个允许 TraceContextBaggage 传播的结构。
outboundMessageCarrier := propagation.NewOutboundMessageCarrier(outboundMessage)
  1. 使用 attribute.KeyValue 切片设置跨度属性,这允许您以键值对的形式将额外的上下文和元数据附加到跨度中。然后使用 trace.SpanStartOption 切片定义新的跨度选项:
attrsForPublishSpan := []attribute.KeyValue{
semconv.MessagingSystem("PubSub+"), // 使用的消息系统
semconv.MessagingDestinationKindTopic, // 发布到主题
semconv.MessagingDestinationName(publishToTopic), // 主题名称
semconv.MessagingOperationPublish, // 消息操作是“发布”
semconv.MessagingMessageID(messageID.String()), // 要发布的消息 ID
}
optsForPublishSpan := []trace.SpanStartOption{
trace.WithAttributes(attrsForPublishSpan...), // 包含跨度属性
trace.WithSpanKind(trace.SpanKindProducer), // 将跨度类型设置为“生产者”
}
  1. (可选)使用键值对创建 Baggage 对象。将 Baggage 添加到上下文实例中:
contextWithBaggage:= context.Background()
m0, _ := baggage.NewMember(string("myKey1"), "value1")
m1, _ := baggage.NewMember(string("myKey2"), "value2")
b, _ := baggage.New(m0, m1)
contextWithBaggage = baggage.ContextWithBaggage(contextWithBaggage, b)
  1. 创建并初始化一个 Tracer,这是开始跨度所必需的,然后使用该追踪器开始一个新的跨度。此跨度使得带有相关元数据和属性的消息发布过程的追踪成为可能。
publisherTracer := otel.GetTracerProvider().Tracer("myAppTracer", trace.WithInstrumentationVersion(Version()))
publishCtx, publishSpan := publisherTracer.Start(context.Background(), fmt.Sprintf("%s publish", publishToTopic), optsForPublishSpan...)

// 如果传播行李,在步骤 4 中创建的 contextWithBaggage 对象传递:
publishCtx, publishSpan := publisherTracer.Start(contextWithBaggage, fmt.Sprintf("%s publish", publishToTopic), optsForPublishSpan...)
  1. 使用 defer 在开始跨度后立即调用 End() 函数。在周围函数结束之前,即使您的应用程序崩溃或有多个返回,这段代码也会执行。
defer publishSpan.End()
  1. 将上下文注入当前跨度:
otel.GetTextMapPropagator().Inject(publishCtx, outboundMessageCarrier)
  1. 发布消息:
publishErr := persistentPublisher.Publish(outMessage, resource.TopicOf(publishToTopic), nil, nil)
if publishErr != nil {
panic(publishErr)
}

有关消息发布者中分布式追踪的完整示例,请参阅 Solace 开发者中心的 publisher.go

在消息接收时生成接收跨度

您的消费应用程序可以生成接收跨度,然后将其导出到 OpenTelemetry 收集器。以下步骤展示了如何从接收到的消息中提取追踪上下文并生成接收跨度:

  1. 初始化并创建一个导出器,它允许您将数据导出到 OpenTelemetry 后端。然后,设置一个 traceProvider,它管理您的应用程序中追踪器的生命周期。在您的应用程序能够开始接收有效的 spanContext 数据之前,需要初始化 traceProvider
exporter, _ := stdouttrace.New(stdouttrace.WithPrettyPrint())
batchspanProcessor := sdktrace.NewSimpleSpanProcessor(exporter) // 在生产环境中,您应该使用批量跨度处理器
traceProvider := sdktrace.NewTracerProvider(
sdktrace.WithSampler(sdktrace.AlwaysSample()),
sdktrace.WithSpanProcessor(batchspanProcessor),
)
  1. 初始化并创建 TraceContext 和可选的 Baggage 传播器实例,并使用 SetTextMapPropagator() 函数将它们注册。此函数使 TraceContextBaggage 结构在您的应用程序中可用。使用 SetTracerProvider() 函数注册您的全局追踪器提供程序,这确保了您代码中创建的所有追踪器都使用 traceProvider 中指定的设置:
otel.SetTextMapPropagator(otel_propagation.TraceContext{})
otel.SetTracerProvider(traceProvider)

// 如果您使用行李传播:
otel.SetTextMapPropagator(propagation.NewCompositeTextMapPropagator(otel_propagation.TraceContext{}, otel_propagation.Baggage{}))
otel.SetTracerProvider(traceProvider)
  1. MessageHandler 回调中,创建一个 NewInboundMessageCarrier 实例,这是一个允许 TraceContextBaggage 传播的结构。接下来,创建您的上下文实例,并使用 OpenTelemetry 的 GetTextMapPropagator()Extract() 函数从 NewInboundMessageCarrier 中检索追踪上下文信息。提取的 TraceContext 然后可以与后续的出站消息一起传播。context.Background() 创建一个基础上下文,将提取的 TraceContext 添加到其中。
var messageHandler solace.MessageHandler = func(message message.InboundMessage) {
inboundMessageCarrier := propagation.NewInboundMessageCarrier(message)
parentSpanContext := otel.GetTextMapPropagator().Extract(context.Background(), inboundMessageCarrier)
  1. (可选)使用 baggage.FromContext() 函数从当前跨度中提取任何 Baggage
extractedBaggage := baggage.FromContext(parentSpanContext)
  1. 使用 attribute.KeyValue 切片设置跨度属性,这允许您以键值对的形式将额外的上下文和元数据附加到跨度中。然后使用 trace.SpanStartOption 切片定义新的跨度选项:
attrs := []attribute.KeyValue{
semconv.MessagingSystem("PubSub+"), // 使用的消息系统
semconv.MessagingDestinationKindTopic, // 使用主题订阅
semconv.MessagingDestinationName(message.GetDestinationName()), // 主题名称
semconv.MessagingOperationReceive, // 消息操作是“接收”
semconv.MessagingMessageID(messageID.String()), // 要发布的消息 ID
}
opts := []trace.SpanStartOption{
trace.WithAttributes(attrs...), // 包含跨度属性
trace.WithSpanKind(trace.SpanKindConsumer), // 将跨度类型设置为“消费者”
}
  1. 创建并初始化一个 Tracer,这是开始跨度所必需的,然后使用该追踪器开始一个新的跨度。此跨度使得带有相关元数据和属性的消息处理过程的追踪成为可能。
tracer := otel.GetTracerProvider().Tracer("myAppTracer", trace.WithInstrumentationVersion(Version()))
receiveCtx, span := tracer.Start(parentSpanContext, fmt.Sprintf("%s receive", message.GetDestinationName()), opts...)
  1. 使用 defer 在开始跨度后立即调用 End() 函数。在周围函数结束之前,即使您的应用程序崩溃或有多个返回,这段代码也会执行。在 Go API 中,默认设置为启用 PersistentReceiverAutoAck,在 End() 函数返回后,消息将被确认。
defer span.End()
  1. 处理接收到的消息:
var messageBody string
if payload, ok := message.GetPayloadAsString(); ok {
messageBody = payload
} else if payload, ok := message.GetPayloadAsBytes(); ok {
messageBody = string(payload)
}
fmt.Printf("Received Message Body %s \n", messageBody)

有关消息订阅者中分布式追踪的完整示例,请参阅 Solace 开发者中心的 subscriber.go