Spring WebFlux 反应式编程
Mono
// 传统方式
@GetMapping("/user/{id}")
public User getUser(@PathVariable String id) {
return userService.findUserById(id); // 阻塞直到返回
}
// 响应式方式
@GetMapping("/user/{id}")
public Mono<User> getUser(@PathVariable String id) {
// 没有立即执行,只是返回了一个描述这个异步操作的 Mono 对象
// 真正的执行发生在:WebFlux 框架发现 Controller 返回了 Mono,框架自动订阅这个 Mono,
// 当 Mono 的值 ready 时,框架把结果写到 HTTP 响应中
return userService.findUserById(id); // 非阻塞,延迟执行
}
Mono 本身是惰性的,不会自动执行逻辑,只有在被 subscribe() 时才会执行。
客户端最终 "看起来" 是立刻得到了结果,这是因为 WebFlux 在背后处理了整个异步链,并在数据到达时才 "触发" 响应输出。所以虽然服务端是异步非阻塞执行,但是客户端并不知道是用 Mono 还是传统方式,它只是发送 HTTP 请求,然后等响应回来。
Mono 常用方法
创建操作
- just(T data) - 创建一个包含给定值的 Mono
- justOrEmpty(@Nullable T data) - 创建可能为空的 Mono
- fromCallable(Callable<? extends T> supplier) - 从 Callable 创建 Mono
- fromFuture(CompletableFuture<? extends T> future) - 从 Future 创建 Mono
- fromRunnable(Runnable runnable) - 从 Runnable 创建 Mono
- empty() - 创建一个立即完成的空 Mono
- error(Throwable error) - 创建一个立即错误终止的 Mono
- never() - 创建一个不发射任何数据的 Mono
- delay(Duration duration) - 创建一个在延迟后完成的 Mono
转换操作
- map(Function<? super T, ? extends V> mapper) - 将值转换为其他值
- flatMap(Function<? super T, ? extends Mono<? extends V>> transformer) - 将值转换为 Mono
- flatMapMany(Function<? super T, ? extends Publisher<? extends V>> mapper) - 将 Mono 转换为 Flux
- filter(Predicate<? super T> tester) - 过滤值
- defaultIfEmpty(T defaultV) - 如果为空则提供默认值
- switchIfEmpty(Mono<? extends T> alternate) - 如果为空则切换到备用 Mono
组合操作
- zipWith(Mono<? extends T2> other, BiFunction<? super T, ? super T2, ? extends R> combinator) - 与另一个 Mono 组合
- then(Mono
other) - 完成后执行另一个 Mono - thenReturn(V value) - 完成后返回给定值
- thenMany(Publisher
other) - 完成后执行 Flux
错误处理
- onErrorResume(Function<? super Throwable, ? extends Mono<? extends T>> fallback) - 错误时恢复
- onErrorReturn(T fallbackValue) - 错误时返回默认值
- onErrorMap(Function<? super Throwable, ? extends Throwable> mapper) - 转换错误
- retry() - 重试订阅
- retryWhen(Function, ? extends Publisher> whenFactory) - 条件重试
其他操作
- subscribe() - 订阅 Mono
- subscribe(Consumer<? super T> consumer) - 订阅并处理值
- block() - 阻塞直到值到达
- log() - 记录所有信号
- doOnSuccess(Consumer<? super T> onSuccess) - 成功时的副作用
- doOnError(Consumer<? super Throwable> onError) - 错误时的副作用
- doOnSubscribe(Consumer<? super Subscription> onSubscribe) - 订阅时的副作用
- doOnCancel(Runnable onCancel) - 取消时的副作用
Flux
HTTP SSE (Server-Sent Events) 是一种在 HTTP 单连接下由服务端持续推送数据的机制。
// Spring WebFlux 中 SSE 的实现
@GetMapping(value = "/sse", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<String> streamData() {
// 客户端每隔一秒就收到一次新的数据,可以感知到响应式行为
return Flux.interval(Duration.ofSeconds(1))
.map(i -> "当前时间:" + Instant.now());
}
Flux 常用方法
创建操作
- just(T... data) 创建一个包含给定元素(基础数据、对象、列表等) 的 Flux,元素作为整体一次性返回
- fromIterable(Iterable<? extends T> it) 从 Iterable 创建 Flux
- fromArray(T[] array) - 从数组创建 Flux,数组作为整体一次性返回
- fromStream(Stream<? extends T> s) - 从 Stream 创建 Flux
- range(int start, int count) - 创建一个包含整数范围的 Flux
- interval(Duration period) - 创建一个定期发射递增 Long 值的 Flux
- empty() - 创建一个立即完成的空 Flux
- error(Throwable error) - 创建一个立即错误终止的 Flux
- never() - 创建一个不发射任何数据的 Flux
转换操作
- map(Function<? super T, ? extends V> mapper) - 将元素转换为其他值
- flatMap(Function<? super T, ? extends Publisher<? extends V>> mapper) - 将每个元素转换为 Publisher 并扁平化
- concatWith(Publisher<? extends T> other) - 与另一个 Publisher 连接
- mergeWith(Publisher<? extends T> other) - 与另一个 Publisher 合并
- filter(Predicate<? super T> predicate) - 过滤元素
- distinct() - 去除重复元素
- take(long n) - 只取前 n 个元素
- skip(long n) - 跳过前 n 个元素
- buffer(int maxSize) - 将元素缓冲为集合
- window(int maxSize) - 将元素窗口化为 Flux 的 Flux
- zipWith(Publisher<? extends T2> source2, BiFunction<? super T, ? super T2, ? extends V> combinator) - 与另一个 Publisher 压缩
组合操作
- zip(Publisher<? extends I> source1, Publisher<? extends I2> source2) - 将多个 Publisher 压缩为一个
- merge(Publisher<? extends T>... sources) - 合并多个 Publisher
- concat(Publisher<? extends T>... sources) - 顺序连接多个 Publisher
错误处理
- onErrorResume(Function<? super Throwable, ? extends Publisher<? extends T>> fallback) - 错误时恢复
- onErrorReturn(T fallbackValue) - 错误时返回默认值
- onErrorMap(Function<? super Throwable, ? extends Throwable> mapper) - 转换错误
- retry() - 重试订阅
- retryWhen(Function, ? extends Publisher> whenFactory) - 条件重试
其他操作
- subscribe() - 订阅 Flux
- subscribe(Consumer<? super T> consumer) - 订阅并处理元素
- blockFirst() - 阻塞直到第一个元素到达
- blockLast() - 阻塞直到最后一个元素到达
- log() - 记录所有信号
- doOnNext(Consumer<? super T> onNext) - 元素到达时的副作用
- doOnComplete(Runnable onComplete) - 完成时的副作用
- doOnError(Consumer<? super Throwable> onError) - 错误时的副作用
Flux 和 Mono 都继承自 Publisher 接口,共享 subscribe 方法
反应式技术组件关系
Project Reactor
[Spring实现] Reactor 开源项目
Reactive Stream
Spring 反应式技术对比
想在 Spring 中实现响应式编程,需要使用到 Spring WebFlux,该组件是一个重新构建的基于 Reactive Streams 标准实现的异步非阻塞 Web 开发框架,以 Reactor 开发框架为基础,可以更加容易地实现高并发访问下的请求处理模型。
[理论] Servlet 3.x 异步编程 -> [Spring实现] Reactor 开源项目 -> [产品] WebFlux
传统请求处理:在 Servlet3.0 标准以前,每一个 Sevlet 都是采用 "Thread-Per-Request" (每个请求对应一个处理线程) 的方式进行请求处理。
异步请求处理:Servlet3.0 标准之后为了解决此类问题,提供了异步响应的支持。将耗时的操作部分交由一个专属的异步线程进行响应处理,同时请求线程资源将被释放,将该线程返回到线程池中。