跳到主要内容

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

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

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

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

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

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

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

依赖项

要启用分布式追踪的上下文传播,您必须首先将 Solace PubSub+ OpenTelemetry 集成 For Solace Java API 库作为依赖项添加到您的应用程序中。此库自动添加 OpenTelemetry API 库,这些库是上下文传播所必需的。有关 OpenTelemetry 版本兼容性的信息,请参阅分布式追踪版本兼容性。添加这些库使您能够访问以下两个接口:

  • SolacePubSubPlusJavaTextMapSetter — 此接口允许 TextMapPropagator 将上下文注入到消息中。

  • SolacePubSubPlusJavaTextMapGetter — 此接口允许 TextMapPropagator 从消息中提取上下文。

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

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

import com.solace.messaging.trace.propagation.SolacePubSubPlusJavaTextMapGetter; // 允许 TextMapPropagator 从消息中提取上下文。
import com.solace.messaging.trace.propagation.SolacePubSubPlusJavaTextMapSetter; // 允许 TextMapPropagator 将上下文注入到消息中。
import io.opentelemetry.api.baggage.Baggage; // 用于管理分布式上下文传播数据
import io.opentelemetry.api.baggage.propagation.BaggageUtil; // 用于处理 Baggage 的实用函数
import io.opentelemetry.api.OpenTelemetry; // OpenTelemetry API 的入口点
import io.opentelemetry.api.trace.Span; // 用于表示追踪中的单个操作
import io.opentelemetry.api.trace.SpanKind; // Span 在追踪中的角色的枚举,例如 SERVER 或 CLIENT
import io.opentelemetry.api.trace.StatusCode; // 用于指示 Span 成功或失败的状态码的枚举
import io.opentelemetry.api.trace.Tracer; // 用于创建和管理 Spans 的接口
import io.opentelemetry.context.Context; // 用于跨线程和异步回调管理上下文传播
import io.opentelemetry.context.Scope; // 用于控制当前上下文范围
import io.opentelemetry.context.propagation.TextMapPropagator; // 用于在基于文本的载体中注入和提取上下文数据的接口
import io.opentelemetry.semconv.trace.attributes.SemanticAttributes; // 追踪的预定义属性键
import io.opentelemetry.semconv.trace.attributes.SemanticAttributes.MessagingDestinationKindValues; // 消息目的地类型的常量
import io.opentelemetry.semconv.trace.attributes.SemanticAttributes.MessagingOperationValues; // 消息操作的常量

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

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

  1. 创建一个新的跨度,并使用 setAttribute() 方法设置跨度属性。接下来,使用 setParent() 方法将当前上下文设置为该跨度的父级。然后,使用 startSpan() 方法启动跨度:
final Span sendSpan = tracer
.spanBuilder("mySolacePublisherApp" + " " + MessagingOperationValues.PROCESS)
.setSpanKind(SpanKind.CLIENT)
.setAttribute(SemanticAttributes.MESSAGING_DESTINATION_KIND, MessagingDestinationKindValues.TOPIC)
// 根据需要设置更多属性
//.setAttribute(...)
.setParent(Context.current())
.startSpan();
  1. (可选)创建一个 Baggage 对象:
Baggage baggage = BaggageUtil.extractBaggage("key1=val1,key2=val2");
  1. 将步骤 1 中创建的跨度(在此示例中为 sendSpan)设置为新的当前上下文。使用 try-with-resources 语句,以确保即使发生异常,跨度也能正确启动和结束:
try (Scope spanScope = sendSpan.makeCurrent()) {
  1. (可选)将步骤 2 中的行李附加到当前上下文。使用 try-with-resources 语句,以确保行李正确地与当前上下文关联,并且仅在 try-with-resources 语句的持续时间内处于活动状态:
    try (Scope baggageScope = baggage.storeInContext(Context.current()).makeCurrent()) {
  1. 创建您的 TextMapSetterTextMapPropagator 对象,这些对象允许您将上下文注入消息,然后在分布式系统中传播该上下文。使用 TextMapPropagatorinject() 方法将当前上下文(及其附加的 sendSpan 和可选的行李)注入到您的消息中:
            final SolacePubSubPlusJavaTextMapSetter setter = new SolacePubSubPlusJavaTextMapSetter();
final TextMapPropagator propagator = openTelemetry.getPropagators().getTextMapPropagator();
propagator.inject(Context.current(), message, setter);
  1. 发布消息,根据需要处理异常,并在跨度上调用 end() 方法以导出跨度数据:
           messagePublisher.publish(message, messageDestination);
}
} catch (Exception e) {
sendSpan.recordException(e); // 如果有任何异常,跨度可以记录异常
sendSpan.setStatus(StatusCode.ERROR, e.getMessage()); // 将跨度状态设置为 ERROR/FAILED
} finally {
sendSpan.end();
}

有关消息发布者中分布式追踪的完整示例,请参阅 Solace 开发者中心的 howToCreateSpanAndBaggageOnMessagePublish() 方法。

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

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

  1. 创建一个 SolacePubSubPlusJavaTextMapGetter 对象,并使用 extract() 方法从接收到的消息中提取上下文:
final SolacePubSubPlusJavaTextMapGetter getter = new SolacePubSubPlusJavaTextMapGetter();
final Context extractedContext = openTelemetry.getPropagators().getTextMapPropagator()
.extract(Context.current(), receivedMessage, getter);
  1. (可选)使用 fromContextOrNull() 方法从提取的上下文中提取行李:
Baggage baggage = Baggage.fromContextOrNull(extractedContext);
  1. 将提取的上下文设置为当前上下文。使用 try-with-resources 语句,以确保即使发生异常,跨度也能正确启动和结束:
try (Scope spanScope = extractedContext.makeCurrent()) {
  1. 创建一个新的跨度,并使用 setAttribute() 方法设置跨度属性。接下来,通过调用 setParent() 方法,使用提取的跨度上下文创建父子关系。然后,使用 startSpan() 方法启动跨度:
    final Span receiveSpan = tracer
.spanBuilder("mySolaceReceiverApp" + " " + MessagingOperationValues.RECEIVE)
.setSpanKind(SpanKind.CLIENT)
.setAttribute(SemanticAttributes.MESSAGING_DESTINATION_KIND, MessagingDestinationKindValues.TOPIC)
// 根据需要设置更多属性
//.setAttribute(...)
.setParent(extractedContext)
.startSpan();
  1. 处理消息,根据需要处理异常,并在跨度上调用 end() 方法以导出跨度数据:
    try {
messageProcessor.accept(receivedMessage);
} catch (Exception e) {
receiveSpan.recordException(e); // 如果有任何异常,跨度可以记录异常
receiveSpan.setStatus(StatusCode.ERROR, e.getMessage()); // 并将跨度状态设置为 ERROR/FAILED
} finally {
receiveSpan.end();
}
}

有关消息订阅者中分布式追踪的完整示例,请参阅 Solace 开发者中心的 howToCreateNewSpanOnMessageReceive() 方法。