Quarkus使用Reactive RestEasy与GraphQlClient时出现线程阻塞问题
问题根本原因
你当前使用的quarkus-resteasy-reactive组件默认将REST接口运行在非阻塞I/O线程池上,该线程池的线程不允许执行任何阻塞操作。你代码中的entityRepository.createRevision(formData)是调用GraphQL客户端的同步阻塞API,直接在I/O线程执行会导致线程被占满卡住,表现为日志已经打印但实际网络请求没有被调度发送。
你添加@Blocking注解后功能正常,是因为该注解会让接口调度到支持阻塞操作的工作线程池运行,不会占用I/O线程。
你之前将createRevision返回值改为Uni仍然失效,大概率是方法内部依然调用了阻塞API(比如调用GraphQL客户端的同步execute方法,或者使用.block()主动阻塞等待Uni结果),本质还是阻塞了I/O线程。
两种可行解决方案
方案1:保留@Blocking注解(推荐,实现成本最低)
如果你的业务逻辑中本身就存在不可避免的阻塞操作,直接在Controller方法上添加@Blocking注解即可,这是Quarkus官方提供的标准方案,完全符合开发规范,不需要强行改造为全异步流程。
方案2:改造为全响应式非阻塞流程
如果需要保持全异步非阻塞的调用链,需要将GraphQL客户端的调用也改为异步API:
- 确认
entityRepository.createRevision内部使用SmallRye GraphQL客户端的异步API,比如调用executeUni()而非同步的execute()方法,方法返回Uni<RevisionResponse>,全程不出现任何阻塞操作。 - 调整Service层代码,将两段异步逻辑通过响应式操作符串接:
public Uni<DocumentResponse> uploadFile(@NonNull FormData formData) { // 异步调用GraphQL创建版本 return entityRepository.createRevision(formData) // 拿到版本结果后执行异步S3上传 .onItem().transformToUni(revisionResponse -> Uni.createFrom().future(storageProviderFactory.getDefaultStorageProvider().upload(formData)) // 上传完成后组装返回对象 .map(uploadRes -> DocumentResponse.builder() .id(revisionResponse.getId()) .entityMasterId(revisionResponse.getEntityMasterId()) .type(revisionResponse.getType()) .path(formData.getFilePath()) .description(formData.getDescription()) .build()) ); }
核心注意事项
Quarkus响应式模型下,所有非@Blocking标注的方法都运行在I/O线程上,严禁执行任何阻塞操作,包括同步HTTP调用、同步数据库访问、Thread.sleep()、主动调用.block()等待响应式结果等,一旦出现阻塞就会导致线程资源耗尽,请求挂起。
内容的提问来源于stack exchange,提问作者Long Nguyen
相关产品推荐
相关产品推荐

