使用Jakarta ClientBuilder调用第三方API时遭遇BlockingNotAllowedException
解决Vert.x事件循环线程阻塞问题
问题根源
你遇到的异常是因为fetchUserInfo方法里的同步HTTP调用(get(JSONObject.class))在Vert.x事件循环线程上执行了阻塞IO操作,之前仅在控制器级别添加@Blocking/@NonBlocking注解无效,是因为实际执行阻塞逻辑的是WenTokenInfoResolver类的方法,控制器的注解无法覆盖这个独立类的方法执行线程。
解决方案
方案1:给阻塞方法/类添加@Blocking注解
直接在执行阻塞IO的fetchUserInfo方法或者整个WenTokenInfoResolver类上添加@io.smallrye.common.annotation.Blocking注解,让该方法被调度到Vert.x的工作线程池执行:
import io.smallrye.common.annotation.Blocking; import static jakarta.ws.rs.core.HttpHeaders.AUTHORIZATION; import jakarta.ws.rs.client.Client; import jakarta.ws.rs.client.ClientBuilder; public class WenTokenInfoResolver { private final Client client; public WenTokenInfoResolver() { client = ClientBuilder.newBuilder().register(JacksonFeature.class).build(); } @Blocking public UserInfo fetchUserInfo(final String userToken) throws WenTokenInfoResolverFailureException { return UserInfo.fromResponse(client.target(ConfigHelper.wenBaseUrl) .path(ConfigHelper.wenInfoOAuth) .request() .header(AUTHORIZATION, "Bearer " + userToken) .get(JSONObject.class)); } }
方案2:改用异步HTTP调用(推荐)
使用Jakarta Client的异步API,避免阻塞事件循环线程,无需添加@Blocking注解:
import java.util.concurrent.CompletionStage; import static jakarta.ws.rs.core.HttpHeaders.AUTHORIZATION; import jakarta.ws.rs.client.Client; import jakarta.ws.rs.client.ClientBuilder; public class WenTokenInfoResolver { private final Client client; public WenTokenInfoResolver() { client = ClientBuilder.newBuilder().register(JacksonFeature.class).build(); } public CompletionStage<UserInfo> fetchUserInfo(final String userToken) { return client.target(ConfigHelper.wenBaseUrl) .path(ConfigHelper.wenInfoOAuth) .request() .header(AUTHORIZATION, "Bearer " + userToken) .rx() // 启用反应式异步API .get(JSONObject.class) .thenApply(UserInfo::fromResponse); } }
调用该方法时,需处理CompletionStage的异步结果,比如在控制器方法中直接返回CompletionStage<UserInfo>,框架会自动处理异步调度。
方案3:使用Vert.x适配的Jakarta Client
如果项目基于Quarkus等Vertx生态框架,推荐使用VertxClientBuilder创建客户端,更好适配Vert.x异步模型:
import io.vertx.mutiny.core.Vertx; import io.vertx.mutiny.ext.web.WebClient; import io.vertx.ext.web.client.WebClientOptions; import jakarta.enterprise.context.ApplicationScoped; @ApplicationScoped public class WenTokenInfoResolver { private final WebClient webClient; public WenTokenInfoResolver(Vertx vertx) { this.webClient = WebClient.create(vertx, new WebClientOptions() .setDefaultHost(ConfigHelper.wenBaseUrl) .setDefaultPort(80)); // 根据实际服务端口调整 } public Uni<UserInfo> fetchUserInfo(final String userToken) { return webClient.get(ConfigHelper.wenInfoOAuth) .putHeader(AUTHORIZATION, "Bearer " + userToken) .send() .onItem().transform(response -> { JSONObject json = response.bodyAsJsonObject(); return UserInfo.fromResponse(json); }); } }
内容的提问来源于stack exchange,提问作者hemanth reddy m
相关产品推荐
相关产品推荐

