【问题标题】:Catch commit exception in Spring jms listener在 Spring jms 监听器中捕获提交异常
【发布时间】:2021-10-02 11:55:11
【问题描述】:

我正在使用 Spring @JmsListener 来使用来自 IBM MQ 队列的消息(在 Docker 容器中运行)。我测试一个场景如下:

  1. 通过网络浏览器向 MQ 发送消息
  2. 我的 Spring 服务将使用从消息中提取的数据并将其发送到存根 (wiremock)
  3. 服务收到成功响应并正常退出

在一个愉快的情况下,消息将被提交并从队列中删除,现在我在存根中添加响应延迟(例如 30 秒),当服务等待响应时,我退出 Docker 以模拟网络问题或 MQ 关闭(docker停止导致 MQ 停顿,这不是我的预期)。

所以我这里有 2 个问题?

  1. 如何捕捉 DefaultMessageListenerContainer 抛出的提交异常?
  2. 我使用 DefaultJmsListenerContainerFactory::setExceptionListener() 方法来附加一个监听器,我可以在此处记录异常,但不会记录 logback MDC(MDC 包含用于审计目的的 messageId 和有效负载)。如何将 MDC 值传递给此侦听器?

源代码:

@Bean
public DefaultJmsListenerContainerFactory containerFactory() {
  DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory();
  factory.setConnectionFactory(createConnectionFactory());
  factory.setExceptionListener(exceptionListener());
  factory.setSessionAcknowledgeMode(javax.jms.Session.CLIENT_ACKNOWLEDGE);
  return factory;
}

@Bean
public ExceptionListener exceptionListener() {
  return exception -> {
    if (exception.getCause() instanceof InterruptedException) {
      // MDC value is not logged in below log.error()
      log.error("commit error");
    } else {
      log.error("jms connection error");
    }
  }
}

---

@JmsListener(id="0", destination="DEV.QUEUE.1", containerFactory="containerFactory")
public void listener(Message msg) {
  try {
    // extract data
    MDC.put("key", value); // for audit
    // call stub
  } catch (JMSException e) {
    throw new Exception(e); // throw exception, don't commit
  }
}

任何帮助将不胜感激。

【问题讨论】:

  • 我没有看到任何设置事务的代码,或消息获取和提交之间的延迟,或提交。
  • @daury 如果您在 MQ 队列管理器 docker 实例上运行 docker stop,那么您的队列管理器将停止。如果您终止该过程,那么它将终止。您可以使用docker network disconnect 来模拟各种容器之间的网络中断吗?您看到正在处理的消息的JMSXDeliveryCount 增加了吗?
  • @chughts 我这里没有设置事务,如果监听器正常返回无异常,则消息被提交并从队列中移除,否则将被移动到DLQ - 此行为在IBM MQ中配置.
  • @richc JMSXDeliveryCount 收到消息时为1,断开连接后仍为1,但运行docker network disconnect时我的服务没有抛出异常
  • 当我运行dis qs(DEV.QUEUE.1) 时,它返回 UNCOM(1),但过了一会儿,它返回 UNCOM(NO)

标签: jms ibm-mq spring-jms


【解决方案1】:
  1. 我可以在错误处理程序中捕获 JMSException
@Bean
public DefaultJmsListenerContainerFactory containerFactory() {
  ...
  factory.setErrorHandler(errorHandler());
}

@Bean
public ErrorHandler errorHandler() {
  return exception -> {
    if (exception.getMessage().contains("session has already been closed")) {
      // log something here
    }
  }
}
  1. 在我的侦听器方法中有一个 MDC.clear(),这就是我丢失 MDC 字段的原因。删除它可以解决问题。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2023-03-23
    • 2023-03-26
    • 2016-02-28
    • 2018-06-14
    • 1970-01-01
    • 1970-01-01
    • 2016-12-15
    相关资源
    最近更新 更多