You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.15 15:23:18