Quarkus下JMS(ActiveMQ Artemis)链路追踪上下文传播问题求助
解决ActiveMQ Artemis JMS微服务的OpenTelemetry链路追踪问题
针对你遇到的两个核心问题,以下是具体的解决方案:
1. 解决JMS属性名非法问题(uber-trace-id包含'-')
JMS规范要求消息属性名必须符合Java标识符规则(不能包含'-'等特殊字符),而OpenTelemetry默认的trace上下文键包含'-',因此需要自定义文本映射传播器,将trace上下文的键转换为符合JMS规范的格式:
自定义Propagator配置
创建自定义的TextMapPropagator,将默认的trace上下文键替换为合法名称(用下划线代替连字符):
import io.opentelemetry.api.trace.propagation.W3CTraceContextPropagator; import io.opentelemetry.context.propagation.TextMapPropagator; import io.opentelemetry.context.propagation.TextMapSetter; import io.opentelemetry.context.propagation.TextMapGetter; import javax.jms.Message; import javax.jms.JMSException; import java.util.ArrayList; import java.util.Collections; import java.util.Enumeration; import java.util.List; public class JmsTracePropagator { private static final TextMapPropagator PROPAGATOR = W3CTraceContextPropagator.getInstance(); // 发送消息时的Setter:转换键名 public static final TextMapSetter<Message> JMS_SETTER = (carrier, key, value) -> { String jmsSafeKey = key.replace("-", "_"); try { carrier.setStringProperty(jmsSafeKey, value); } catch (JMSException e) { // 日志记录异常,避免影响业务流程 e.printStackTrace(); } }; // 接收消息时的Getter:转换回原键名 public static final TextMapGetter<Message> JMS_GETTER = new TextMapGetter<>() { @Override public String get(Message carrier, String key) { String jmsSafeKey = key.replace("-", "_"); try { return carrier.getStringProperty(jmsSafeKey); } catch (JMSException e) { return null; } } @Override public Iterable<String> keys(Message carrier) { try { Enumeration<String> propNames = carrier.getPropertyNames(); List<String> keys = new ArrayList<>(); while (propNames.hasMoreElements()) { String propName = propNames.nextElement(); keys.add(propName.replace("_", "-")); } return keys; } catch (JMSException e) { return Collections.emptyList(); } } }; public static TextMapPropagator getPropagator() { return PROPAGATOR; } }
2. 解决消息属性只读问题
ActiveMQ Artemis的部分消息类型(比如从队列接收的Core Message)默认是只读的,此时需要复制消息为可编辑实例再操作,或者在发送时确保创建可修改的消息:
发送端处理(确保消息可编辑)
发送时直接创建标准JMS消息类型并设置属性:
import javax.jms.*; // 获取JMS连接和会话 Connection connection = connectionFactory.createConnection(); Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); Queue queue = session.createQueue("your-target-queue"); MessageProducer producer = session.createProducer(queue); // 创建可编辑的文本消息 TextMessage message = session.createTextMessage("your-business-content"); // 注入trace上下文 io.opentelemetry.context.Context currentContext = io.opentelemetry.context.Context.current(); JmsTracePropagator.getPropagator().inject(currentContext, message, JmsTracePropagator.JMS_SETTER); // 发送消息 producer.send(message);
接收端处理(处理只读消息)
如果接收的消息是只读的,先复制为新的可编辑实例再提取trace上下文:
import javax.jms.*; MessageConsumer consumer = session.createConsumer(queue); Message receivedMessage = consumer.receive(); // 复制只读消息为可编辑实例(以TextMessage为例) TextMessage editableMessage = session.createTextMessage(); if (receivedMessage instanceof TextMessage) { editableMessage.setText(((TextMessage) receivedMessage).getText()); // 复制原有属性 Enumeration<String> propNames = receivedMessage.getPropertyNames(); while (propNames.hasMoreElements()) { String propName = propNames.nextElement(); editableMessage.setObjectProperty(propName, receivedMessage.getObjectProperty(propName)); } } // 提取trace上下文并创建Span io.opentelemetry.context.Context extractedContext = JmsTracePropagator.getPropagator() .extract(io.opentelemetry.context.Context.current(), editableMessage, JmsTracePropagator.JMS_GETTER); try (io.opentelemetry.context.Scope scope = extractedContext.makeCurrent()) { io.opentelemetry.api.trace.Span span = io.opentelemetry.api.trace.Span.current(); span.setName("process-jms-message"); // 执行消息处理逻辑 span.end(); }
3. 简化方案:使用OpenTelemetry JMS自动Instrumentation
如果手动实现繁琐,推荐使用官方JMS自动Instrumentation,只需添加依赖并配置键映射:
- 添加Maven依赖:
<dependency> <groupId>io.opentelemetry.instrumentation</groupId> <artifactId>opentelemetry-jms-2.0</artifactId> <version>1.28.0</version> <!-- 使用最新稳定版 --> </dependency>
- 启动时配置键映射(避免非法字符):
// 将W3C标准的trace上下文键转换为JMS合法名称 System.setProperty("otel.propagators", "tracecontext"); System.setProperty("otel.jms.propagation.key.map", "traceparent=trace_parent,tracestate=trace_state");
内容的提问来源于stack exchange,提问作者sg2000
相关产品推荐
相关产品推荐

