使用quarkus-keycloak-admin-client-reactive时抛出BlockingNotAllowedException求助
排查Quarkus 3.7.3中
BlockingNotAllowedException异常的思路及解决方案 针对你在使用quarkus-keycloak-admin-client-reactive时遇到的异常,以下是具体排查方向和解决方法:
1. 排查依赖冲突
确保项目中仅引入响应式版本的Keycloak Admin Client,避免阻塞版依赖共存:
<!-- 正确依赖 --> <dependency> <groupId>io.quarkus</groupId> <artifactId>quarkus-keycloak-admin-client-reactive</artifactId> </dependency> <!-- 必须移除阻塞版依赖 --> <!-- <dependency> <groupId>io.quarkus</groupId> <artifactId>quarkus-keycloak-admin-client</artifactId> </dependency> -->
2. 检查配置是否意外启用阻塞模式
确认application.properties中未强制开启阻塞客户端模式:
# 确保该配置不存在或设为false(默认值即为false) quarkus.keycloak.admin-client.blocking-enabled=false # 其他基础配置示例 quarkus.keycloak.admin-client.server-url=http://localhost:8080/auth quarkus.keycloak.admin-client.realm=master quarkus.keycloak.admin-client.client-id=admin-cli quarkus.keycloak.admin-client.username=admin quarkus.keycloak.admin-client.password=admin
3. 检查代码中的阻塞操作
禁止在响应式上下文调用block()/await()
如果在无@Blocking注解的响应式方法(如REST接口)中调用Uni的block()或await(),会直接阻塞事件循环线程触发异常:
// 错误示例 @GET @Path("/users") public List<UserRepresentation> getUsers() { // 此处调用await()会阻塞事件循环线程 return keycloak.realm("master").users().list().await().indefinitely(); } // 正确示例:直接返回Uni,由Quarkus处理响应式流 @GET @Path("/users") public Uni<List<UserRepresentation>> getUsers() { return keycloak.realm("master").users().list(); }
避免在Uni回调中执行阻塞操作
Uni的onItem().invoke()/onFailure().invoke()等回调默认在事件循环线程执行,若其中包含同步JDBC、文件IO、第三方同步客户端调用等阻塞操作,会触发异常:
// 错误示例:回调中执行阻塞操作 public Uni<Void> createUser(UserRepresentation user) { return keycloak.realm("master").users().create(user) .onItem().invoke(() -> { // 同步JPA持久化属于阻塞操作 entityManager.persist(new UserLog(user.getUsername())); }); } // 解决方法1:用@Blocking注解标记阻塞方法,Quarkus自动切换到工作线程 public Uni<Void> createUser(UserRepresentation user) { return keycloak.realm("master").users().create(user) .onItem().invoke(() -> logUserCreation(user.getUsername())); } @Blocking public void logUserCreation(String username) { entityManager.persist(new UserLog(username)); } // 解决方法2:用Uni包装阻塞操作并指定工作线程池 public Uni<Void> createUser(UserRepresentation user) { return keycloak.realm("master").users().create(user) .onItem().invoke(() -> { Uni.createFrom().item(() -> entityManager.persist(new UserLog(user.getUsername()))) .runSubscriptionOn(Infrastructure.getDefaultWorkerPool()) .subscribe().with(v -> {}); }); }
4. 线程上下文调试
若以上排查无效,开启Quarkus调试日志,跟踪异常发生时的线程名称:
- 事件循环线程名称通常带有
vert.x-eventloop-前缀 - 工作线程名称带有
quarkus-worker-前缀
通过线程名称确认阻塞操作是否发生在事件循环线程中,进一步定位代码中的隐性阻塞逻辑。
内容的提问来源于stack exchange,提问作者Max
相关产品推荐
相关产品推荐

