Quarkus集成ActiveMQ时如何透传request-id实现日志链路追踪
问题根因
你之前实现的MdcContextProvider属于MicroProfile上下文传播组件的扩展,仅能在同一个应用进程内、被上下文传播组件托管的线程切换场景下传递MDC内容。ActiveMQ的监听器运行在独立的消息拉取工作线程上,和你处理HTTP请求、发送消息的线程没有托管的上下文传播关联,甚至如果消费者是独立部署的服务,两者根本不在同一个进程里,这个方案天然无法生效。
可行实现方案
方案1:将request-id作为JMS消息属性传递(首推,无场景限制)
这是跨线程、跨服务传递请求上下文的标准实现,request-id完全跟随消息本身传输,不依赖线程上下文,不会出现丢失、串号问题:
- 消息发送侧逻辑:在构造JMS消息时,从当前MDC中取出request-id,写入消息自定义属性
// JMS消息发送示例 jmsTemplate.convertAndSend(queueName, payload, message -> { String currentRequestId = MDC.get("requestId"); if (currentRequestId != null) { // 写入消息属性,会随消息持久化、网络传输 message.setStringProperty("X-Request-Id", currentRequestId); } return message; });
- 消息消费侧逻辑:通过监听器包装类或者拦截器,在消费逻辑执行前提取消息属性中的request-id写入MDC,执行完成后清理MDC,避免线程复用导致上下文污染
// MDC上下文传递监听器包装类 public class MdcAwareMessageListener implements MessageListener { private final MessageListener actualListener; private static final String MDC_REQUEST_ID_KEY = "requestId"; private static final String MSG_REQUEST_ID_PROP = "X-Request-Id"; public MdcAwareMessageListener(MessageListener actualListener) { this.actualListener = actualListener; } @Override public void onMessage(Message message) { String requestId = null; try { requestId = message.getStringProperty(MSG_REQUEST_ID_PROP); if (requestId != null) { MDC.put(MDC_REQUEST_ID_KEY, requestId); } // 执行业务实际的消息消费逻辑 actualListener.onMessage(message); } catch (JMSException e) { throw new RuntimeException("消息消费异常", e); } finally { // 必须清理MDC,避免线程池复用时上下文串到其他请求 if (requestId != null) { MDC.remove(MDC_REQUEST_ID_KEY); } } } }
该方案完全兼容发送端、消费端分属不同服务部署的场景,是分布式场景下传递请求标识的通用做法。
方案2:修复现有上下文传播配置(仅适合同进程内收发消息场景)
如果你的消息发送和消费逻辑都运行在同一个应用进程中,可以排查修复现有配置的问题让ThreadContextProvider生效:
- 首先核对SPI配置的类全限定名:你在
org.eclipse.microprofile.context.spi.ThreadContextProvider文件中写的类路径是com.org.common.report.Provider.MdcContextProvider,必须和你实际MdcContextProvider类的包名、类名完全一致,SPI机制是严格匹配全类名加载的,任意字符错误都会导致扩展不生效。 - 其次要给ActiveMQ的工作线程池托管上下文:MicroProfile上下文传播不会自动拦截第三方组件的线程池,你需要将ActiveMQ客户端使用的线程池替换为容器托管的
ManagedExecutorService,或者在提交监听器消费任务时,用ThreadContext.contextualRunnable()包装消费逻辑,否则自定义的ThreadContextProvider不会被触发。
该方案局限性极强,只要后续拆分消费者为独立服务就会完全失效,不推荐优先使用。
方案3:接入标准分布式链路追踪组件
如果服务需要长期迭代、做全链路日志排查,可以直接接入OpenTelemetry等成熟的链路追踪组件,组件会自动在HTTP入口生成请求标识、自动在JMS消息收发时传递上下文、自动给日志埋点对应的请求ID,不需要手动编写属性传递、MDC设置清理的逻辑,后续还可以扩展查看全链路耗时、错误节点等信息。
内容的提问来源于stack exchange,提问作者ss13199
相关产品推荐
相关产品推荐

