以下是一个 **完整的综合示例**,展示如何使用 `Mono.zip` 实现 **非阻塞并行调用**,并结合 **超时控制、异常处理、多个 `flatMap` 链式操作** 等场景。示例基于 Spring WebFlux(Reactor 3.x)。
---
### **场景描述**
1. **并行调用**:同时获取用户基本信息、订单列表和积分(互不依赖)。
2. **超时控制**:订单查询超过 500ms 自动降级。
3. **异常处理**:积分服务失败时返回默认值。
4. **结果聚合**:将所有数据组合为 `UserProfile` 对象。
5. **非阻塞线程**:确保所有操作在弹性线程池执行,不阻塞主线程(如 Netty 事件循环)。
---
### **完整代码**
```java
import reactor.core.publisher.Mono;
import reactor.core.scheduler.Schedulers;
import java.time.Duration;
import java.util.Collections;
// 模拟的实体类
record User(String id, String name) {}
record Order(String id, String product) {}
record Credit(Integer points) {}
record UserProfile(User user, List<Order> orders, Credit credit) {}
public class MonoZipComplexExample {
// 模拟远程服务调用(非阻塞)
private Mono<User> getUserById(String userId) {
return Mono.fromCallable(() -> {
Thread.sleep(100); // 模拟延迟
return new User(userId, "Alice");
}).subscribeOn(Schedulers.boundedElastic()); // 切换到弹性线程池
}
private Mono<List<Order>> getOrdersByUser(String userId) {
return Mono.fromCallable(() -> {
Thread.sleep(600); // 模拟超时(实际 600ms > 500ms 超时阈值)
return List.of(new Order("order1", "Laptop"), new Order("order2", "Phone"));
})
.subscribeOn(Schedulers.boundedElastic())
.timeout(Duration.ofMillis(500)) // 超时控制
.onErrorResume(e -> Mono.just(Collections.emptyList())); // 超时后降级
}
private Mono<Credit> getCreditByUser(String userId) {
return Mono.<Credit>fromCallable(() -> {
if (userId.equals("user1")) {
throw new RuntimeException("Credit service failed!");
}
return new Credit(100);
})
.subscribeOn(Schedulers.boundedElastic())
.onErrorResume(e -> Mono.just(new Credit(0))); // 异常时返回默认值
}
// 综合调用
public Mono<UserProfile> getUserProfile(String userId) {
// 并行获取数据
Mono<User> userMono = getUserById(userId).cache(); // cache() 避免重复订阅
Mono<List<Order>> ordersMono = getOrdersByUser(userId);
Mono<Credit> creditMono = getCreditByUser(userId);
// 使用 zip 并行合并结果
return Mono.zip(userMono, ordersMono, creditMono)
.flatMap(tuple -> {
User user = tuple.getT1();
List<Order> orders = tuple.getT2();
Credit credit = tuple.getT3();
// 模拟后续异步处理(例如写入日志)
return logProfileAsync(user, orders, credit)
.thenReturn(new UserProfile(user, orders, credit));
})
.doOnNext(profile -> System.out.println("Profile loaded: " + profile))
.doOnError(e -> System.err.println("Failed: " + e.getMessage()));
}
private Mono<Void> logProfileAsync(User user, List<Order> orders, Credit credit) {
return Mono.fromRunnable(() -> {
System.out.println("Logging to DB: " + user.name());
}).subscribeOn(Schedulers.boundedElastic()).then();
}
// 测试
public static void main(String[] args) {
MonoZipComplexExample example = new MonoZipComplexExample();
example.getUserProfile("user1")
.subscribe(
profile -> System.out.println("Final Result: " + profile),
error -> System.out.println("Final Error: " + error)
);
// 防止主线程退出(实际在 WebFlux 中不需要)
try { Thread.sleep(2000); } catch (InterruptedException e) {}
}
}
```
---
### **关键点解析**
1. **非阻塞线程控制**
- 所有阻塞操作(如 `Thread.sleep`、网络调用)通过 `subscribeOn(Schedulers.boundedElastic())` 切换到弹性线程池执行,避免阻塞 Reactor 主线程。
2. **超时与降级**
- `getOrdersByUser` 设置 `timeout(Duration.ofMillis(500))`,超时后通过 `onErrorResume` 返回空列表。
3. **异常处理**
- `getCreditByUser` 模拟服务失败时,通过 `onErrorResume` 返回默认值 `new Credit(0)`。
4. **并行合并**
- `Mono.zip(userMono, ordersMono, creditMono)` 并行执行三个 `Mono`,所有结果就绪后触发 `flatMap`。
5. **链式操作**
- `flatMap` 内调用 `logProfileAsync` 展示异步链式操作,最后返回组合结果 `UserProfile`。
6. **日志与监控**
- `doOnNext` 和 `doOnError` 用于记录成功/失败日志。
---
### **输出结果**
根据代码中的延迟和异常模拟,可能的输出:
```text
Logging to DB: Alice
Profile loaded: UserProfile[user=User[id=user1, name=Alice], orders=[], credit=Credit[points=0]]
Final Result: UserProfile[user=User[id=user1, name=Alice], orders=[], credit=Credit[points=0]]
```
---
### **总结**
- **`Mono.zip`**:用于并行合并多个异步任务,提升性能。
- **超时/降级**:通过 `timeout` + `onErrorResume` 保证系统弹性。
- **非阻塞**:始终通过 `subscribeOn` 隔离阻塞操作。
- **链式扩展**:结合 `flatMap` 实现复杂异步流程。
此示例可直接用于 Spring WebFlux 项目或独立的 Reactor 应用中。