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关闭,导致接口响应延迟。
此外,代码存在两个加重问题的细节:
- 每次发送消息调用
context.createQueue("test123"),会向Artemis服务器发起队列查询/创建请求,增加额外网络交互,可能导致Session关闭时存在未完成操作; - 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
相关产品推荐
相关产品推荐

