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

Spring JmsListener捕获MQ提交异常与ExceptionListener透传MDC问题

问题1:捕获DefaultMessageListenerContainer提交异常

你当前使用CLIENT_ACKNOWLEDGE模式时,默认容器会在监听器方法无异常返回后自动执行消息确认,该阶段抛出的异常不会进入你配置的JMS ExceptionListener(仅处理连接级异常),可通过两种方案解决:

方案A:手动确认消息(最推荐,可同时解决MDC丢失问题)

关闭容器自动确认逻辑,在监听器方法内手动执行提交,提交异常直接在当前线程捕获:

@JmsListener(id="0", destination="DEV.QUEUE.1", containerFactory="containerFactory")
public void listener(Message msg) {
  try {
    // 提取数据
    MDC.put("key", value); // 用于审计
    // 调用stub
    // 手动执行消息确认,提交异常直接在当前上下文捕获
    msg.acknowledge();
  } catch (JMSException e) {
    log.error("消息处理或提交失败", e);
    // 按需配置重试/死信队列逻辑,抛出异常会触发消息重发
    throw new RuntimeException(e);
  } finally {
    MDC.clear(); // 必须清除,避免线程池复用导致MDC污染
  }
}

如果开启了事务模式,可注册事务同步回调捕获提交异常,同样可以直接获取当前线程MDC:

TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() {
  @Override
  public void afterCompletion(int status) {
    if (status != STATUS_COMMITTED) {
      log.error("事务提交失败");
    }
  }
});

方案B:自定义ErrorHandler捕获容器级提交异常

如果需要保留容器自动提交逻辑,给容器工厂配置自定义ErrorHandler,提交阶段的异常会传递到该Handler:

@Bean
public DefaultJmsListenerContainerFactory containerFactory() {
  DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory();
  factory.setConnectionFactory(createConnectionFactory());
  factory.setExceptionListener(exceptionListener());
  factory.setSessionAcknowledgeMode(javax.jms.Session.CLIENT_ACKNOWLEDGE);
  // 配置自定义错误处理器
  factory.setErrorHandler(customErrorHandler());
  return factory;
}

@Bean
public ErrorHandler customErrorHandler() {
  return throwable -> {
    log.error("容器处理消息异常", throwable);
  };
}

问题2:MDC值传递到异常监听器

MDC丢失的核心原因是ExceptionListener、ErrorHandler的执行线程和@JmsListener方法的执行线程不是同一个,MDC默认基于ThreadLocal存储,跨线程无法直接读取,可通过以下方案解决:

方案1:配合手动提交使用

就是上述问题1的方案A,提交逻辑和异常处理都在监听器线程执行,MDC天然存在,不需要额外做跨线程传递。

方案2:自定义TaskDecorator传递MDC

如果必须使用全局异常监听器,给监听器线程池配置TaskDecorator,在任务提交时把当前线程的MDC复制到执行线程:

public class MdcTaskDecorator implements TaskDecorator {
  @Override
  public Runnable decorate(Runnable runnable) {
    Map<String, String> mdcContext = MDC.getCopyOfContextMap();
    return () -> {
      try {
        if (mdcContext != null) {
          MDC.setContextMap(mdcContext);
        }
        runnable.run();
      } finally {
        MDC.clear();
      }
    };
  }
}

给容器工厂绑定带MDC装饰器的线程池:

@Bean
public DefaultJmsListenerContainerFactory containerFactory() {
  DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory();
  factory.setConnectionFactory(createConnectionFactory());
  factory.setExceptionListener(exceptionListener());
  factory.setSessionAcknowledgeMode(javax.jms.Session.CLIENT_ACKNOWLEDGE);
  // 配置带MDC传递的线程池
  ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
  executor.setCorePoolSize(5);
  executor.setMaxPoolSize(10);
  executor.setQueueCapacity(25);
  executor.setTaskDecorator(new MdcTaskDecorator());
  executor.initialize();
  factory.setTaskExecutor(executor);
  return factory;
}

内容的提问来源于stack exchange,提问作者dauruy

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.10 14:18:01