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

Quarkus调用JMS Session的close()后代码挂起问题排查

Quarkus JMS接口发送消息后挂起30秒返回的问题排查与解决

问题现象

Java 11 Quarkus项目中,通过REST端点发送JMS消息到ActiveMQ Artemis,调用POST http://localhost:9999/jms发送{"message":"Hello!"}后,日志显示消息已接收并发送,但接口会挂起20-30秒才返回200状态码,日志可见Session在30秒后才完成关闭清理。

代码与配置

REST端点代码

package org.acme.jms;

import javax.inject.Inject;
import javax.ws.rs.Consumes;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Response;

@Path("/jms")
@Produces("application/json")
@Consumes("application/json")
public class JMSEndpoint {

  @Inject
  JMSProducer producer;

  @POST
  public Response sendMessage(String message) {
    System.out.println("recieved request");
    producer.sendMessage(message);

    return Response.status(201).build();
  }
}

JMS生产者代码

package org.acme.jms;

import javax.enterprise.context.ApplicationScoped;
import javax.inject.Inject;
import javax.jms.ConnectionFactory;
import javax.jms.JMSContext;
import javax.jms.JMSRuntimeException;
import javax.jms.Session;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.ResponseBuilder;

@ApplicationScoped
public class JMSProducer {

  @Inject
  ConnectionFactory connectionFactory;

  public ResponseBuilder sendMessage(String message) {
    try (
      JMSContext context = connectionFactory.createContext(
        Session.AUTO_ACKNOWLEDGE
      )
    ) {
      context.createProducer().send(context.createQueue("test123"), message);
      System.out.println("hello");
      return Response.status(201);
    } catch (JMSRuntimeException ex) {
      // handle exception (details omitted)
    }
    return null;
  }
}

pom.xml关键配置

<properties>
    <maven.compiler.target>11</maven.compiler.target>
    <maven.compiler.source>11</maven.compiler.source>
    <quarkus.platform.version>2.12.1.Final</quarkus.platform.version>
    <quarkus-artemis.version>1.2.0</quarkus-artemis.version>
</properties>
<dependencies>
    <dependency>
        <groupId>io.quarkiverse.artemis</groupId>
        <artifactId>quarkus-artemis-jms</artifactId>
    </dependency>
    <dependency>
        <groupId>io.quarkus</groupId>
        <artifactId>quarkus-resteasy-reactive</artifactId>
    </dependency>
</dependencies>

原因分析

从日志30秒的时间差和Session关闭日志来看,核心问题是ActiveMQ Artemis客户端默认Session关闭超时为30秒。JMSContext关闭时,若存在未完成的网络请求或资源清理操作,客户端会等待默认超时后才完成Session关闭,导致接口响应延迟。

此外,代码存在两个加重问题的细节:

  1. 每次发送消息调用context.createQueue("test123"),会向Artemis服务器发起队列查询/创建请求,增加额外网络交互,可能导致Session关闭时存在未完成操作;
  2. Resteasy Reactive是异步非阻塞框架,但端点直接调用阻塞的JMS操作,可能占用事件循环线程,间接影响资源清理效率。

解决方案

1. 缩短Artemis客户端关闭超时时间

在application.properties中添加配置,将Session关闭超时设置为更短时间(例如5秒):

quarkus.artemis.client.close-timeout=5000

2. 提前声明队列,避免动态创建

在application.properties中配置预定义队列:

quarkus.artemis.jms.queues.test123=test123

修改生产者代码,注入预定义队列,避免每次发送时动态创建:

package org.acme.jms;

import javax.enterprise.context.ApplicationScoped;
import javax.inject.Inject;
import javax.jms.ConnectionFactory;
import javax.jms.JMSContext;
import javax.jms.JMSRuntimeException;
import javax.jms.Queue;
import javax.jms.Session;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.ResponseBuilder;

@ApplicationScoped
public class JMSProducer {

  @Inject
  ConnectionFactory connectionFactory;
  
  @Inject
  @JMSQueue("test123")
  Queue testQueue;

  public ResponseBuilder sendMessage(String message) {
    try (JMSContext context = connectionFactory.createContext(Session.AUTO_ACKNOWLEDGE)) {
      context.createProducer().send(testQueue, message);
      System.out.println("hello");
      return Response.status(201);
    } catch (JMSRuntimeException ex) {
      ex.printStackTrace();
      return Response.status(Response.Status.INTERNAL_SERVER_ERROR);
    }
  }
}

3. 优化阻塞操作处理

在REST端点添加@Blocking注解,将阻塞的JMS操作转移到专用线程池,避免占用事件循环线程:

@POST
@Blocking
public Response sendMessage(String message) {
  System.out.println("recieved request");
  ResponseBuilder responseBuilder = producer.sendMessage(message);
  return responseBuilder != null ? responseBuilder.build() : Response.status(500).build();
}

4. 修正响应状态码逻辑

原端点直接返回201,未使用生产者的返回值,无法感知操作异常。修改后根据生产者返回的ResponseBuilder构建响应,确保状态码准确反映操作结果。

验证修改

完成配置与代码调整后重启项目,接口将在消息发送完成后立即返回对应状态码,Session关闭日志也会在短时间内生成,不再出现30秒挂起现象。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.01 09:45:28