如何在Quarkus响应式应用中用Vertx & Mutiny链式调用外部API与DB?
基于Quarkus + Vert.x Mutiny的响应式链式实现方案
一、外部API调用的正确姿势
你用Unirest同步调用再包装成Uni的做法不符合响应式非阻塞原则,同步调用会阻塞Vert.x事件循环,浪费Quarkus响应式模型的优势。应该使用Quarkus官方推荐的io.quarkus.vertx.webclient.ReactiveWebClient——这是Vert.x WebClient的Mutiny适配版,天生支持非阻塞响应式调用,和Quarkus生态集成更顺畅。
二、完整代码实现
1. 依赖配置(pom.xml)
确保引入核心依赖:
<dependency> <groupId>io.quarkus</groupId> <artifactId>quarkus-reactive-web-client</artifactId> </dependency> <dependency> <groupId>io.quarkus</groupId> <artifactId>quarkus-hibernate-reactive-panache</artifactId> </dependency> <dependency> <groupId>io.quarkus</groupId> <artifactId>quarkus-reactive-pg-client</artifactId> </dependency>
2. 数据模型定义
先明确DTO与数据库实体结构:
// 外部API返回的Response DTO public class Response { public String abc; public List<AnotherClass> anotherClasses; } public class AnotherClass { public Long id; // 关联数据库查询的ID public List<Role> roles; } public class Role { // 外部API返回的Role字段,如name、code等 } // Postgres数据库实体 @Entity public class RoleFromDB extends PanacheEntityBase { @Column(name = "another_id") public Long anotherId; // 数据库存储的其他字段,如roleName、roleCode等 }
3. 响应式链式调用核心逻辑
import io.quarkus.vertx.webclient.ReactiveWebClient; import io.smallrye.mutiny.Uni; import jakarta.inject.Inject; import java.util.List; import java.util.stream.Collectors; public class ResponseService { @Inject ReactiveWebClient webClient; public Uni<Response> fetchAndMergeData() { // 1. 响应式调用外部API获取初始Response return webClient.get() .uri("https://external-api/your-target-endpoint") .retrieve() .body(Response.class) // 2. 基于API返回的ID批量查询数据库 .onItem().transformToUni(response -> { // 收集所有需要查询的AnotherClass ID List<Long> anotherIds = response.anotherClasses.stream() .map(AnotherClass::getId) .collect(Collectors.toList()); // 批量查询数据库,避免循环调用提升性能 return RoleFromDB.list("anotherId in ?1", anotherIds) .onItem().transform(dbRoles -> { // 3. 将数据库结果合并到原Response中 response.anotherClasses.forEach(another -> { // 匹配当前AnotherClass对应的数据库角色 List<RoleFromDB> matchedRoles = dbRoles.stream() .filter(role -> role.anotherId.equals(another.getId())) .collect(Collectors.toList()); // 转换数据库实体为API的Role格式并合并 another.roles.addAll(matchedRoles.stream() .map(this::convertToRole) .collect(Collectors.toList())); }); return response; }); }); } // 数据库实体到API Role的字段映射方法 private Role convertToRole(RoleFromDB dbRole) { Role role = new Role(); // 根据实际业务字段映射,例如: // role.setName(dbRole.roleName); // role.setCode(dbRole.roleCode); return role; } }
4. 关键细节说明
- 非阻塞调用:
ReactiveWebClient基于Vert.x事件循环执行,全程不阻塞线程,完全契合Quarkus响应式模型。 - 批量查询优化:通过一次批量查询替代循环单查,减少数据库连接开销,提升性能。
- 链式异步流:用
onItem().transformToUni衔接异步操作,保证整个流程的响应式连续性,避免回调地狱。
三、Axle WebClient兼容方案(不推荐)
如果因特殊需求必须使用io.vertx.axle.ext.web.client.WebClient,需要手动将Axle的CompletionStage转换为Mutiny的Uni:
import io.vertx.axle.ext.web.client.WebClient; import io.smallrye.mutiny.Uni; import jakarta.inject.Inject; public class AxleWebClientDemo { @Inject WebClient axleWebClient; public Uni<Response> fetchWithAxle() { return Uni.createFrom().completionStage( axleWebClient.getAbs("https://external-api/your-endpoint") .send() .thenApply(response -> response.bodyAsJson(Response.class)) ); } }
注意:该方案需要手动处理转换,不如ReactiveWebClient原生适配流畅,优先推荐前者。
内容的提问来源于stack exchange,提问作者Vemai Clan
相关产品推荐
相关产品推荐

