Spring网关与WebFlux-Mono和Flux

1、概述

WebFlux 是 Spring Framework 5 引入的一个模块,它是一个 非阻塞的、异步的、响应式的 Web 开发框架。WebFlux 设计的核心是为了 使用现代 Web 应用对于高并发、低延迟和高吞吐量的需求,它采用 Reactive 编程模型,通过 Reactor 库实现了异步数据流处理。

在 WebFlux 中,HTTP 请求和响应被建模为 Mono(代表 0~1 个元素的异步序列)和 Flux(代表 0~N个元素的异步序列)类型,这些都是 Reactive Streams 规范的一部分。这意味着 开发者可以通过声明式和函数式编程风格来处理请求和响应的数据流。

2、WebFlux 提供了两种编程模型

  1. 注解式控制器: 使用 @Controller 等注解,类似 Spring MVC 的开发体验。
  2. 函数式编程控制器: 使用 Java 8 函数式接口定义路由和处理逻辑。

3、WebFlux 与 Spring MVC 区别

3.1、WebFlux

  1. 异步非阻塞: WebFlux 基于反应式编程模型,支持非阻塞 I/O,能够充分利用多核 CPU 资源,并且在高并发场景下具有更好的性能表现,因为它不会为每个请求分配独立的线程,从而避免了线程上下文切换带来的开销。
  2. 响应式编程: WebFlux 使用 Project Reactor(或者 RxJava 作为备选)提供的 Mono 和 Flux 类型来表示可能零个、一个或多个事件的异步序列,使得开发者可以编写异步数据处理逻辑。
  3. 无需 Servlet API: 尽管可以在 Servlet 容器上运行,但它不直接依赖 Servlet API,能在非阻塞服务器(如 Netty、Undertow 等)上运行。
  4. 函数式编程风格: 除了提供类似于 Spring MVC 的注解式编程模型外,WebFlux 还支持函数式编程模型,允许通过 RouterFunction 等方式进行更灵活的路由配置。

3.2、Spring MVC

  1. 同步阻塞: Spring MVC 基于传统的 Servlet API,每个 HTTP 请求通常都会绑定到一个单独的线程直到请求处理完成并发送响应为止。
  2. 线程模型: 在默认情况下,Spring MVC 应用中,每个请求会创建或从线程池获取一个线程,处理完成后释放回线程池。这种模式在请求处理复杂度较高或线程池大小受限时,可能会影响系统的并发能力。
  3. 依赖 Servlet 容器: Spring MVC 必须部署在支持 Servlet API 的容器中运行(如:Tomcat、Jetty、Undertow、Weblogic等)。
  4. API 和编程模型: Spring MVC 主要采用注解驱动的方式组织控制器和处理请求响应,例如通过 @Controller、@RequestMapping 等注解。

4、通俗易懂说Mono和Flux

4.1、Mono

想象一下,你在网上购物,点击了一个商品的“加入购物车”按钮。这个操作要么成功(你得到了一个确认信息),要么失败(可能商品缺货或网络问题)。Mono就像是这样一个操作的抽象表示,它代表了一个将来可能会发生的事情,这个事情的结果只有一个(成功的信息或失败的原因),或者什么都没有(如果操作没有真正完成)。在编程世界里,这可以是一个数据库查询结果、一次网络请求的响应,或者其他任何可能产生单一结果(包括空结果)的操作。

4.2、Flux

现在,设想你正在浏览商品列表,页面不断向下滚动,商品图片和信息随着你的滚动逐渐加载出来。这个过程不是一个单一的结果,而是一个接一个连续不断的商品信息流。Flux就是用来描述这种场景的,它代表了一个可以产生0到多个元素的异步序列。就像商品列表不断更新一样,Flux可以随着时间推移发射多个数据项,直到没有更多数据为止(比如列表加载完毕,或者用户停止滚动)。在应用中,这可能是一个实时更新的消息队列、持续的数据流监测结果,或者是分页加载的数据列表。

4.3、总结

  • Mono就像是一个快递包裹,里面可能有一个礼物(数据),也可能什么都没有(空包裹),但你不会收到两个礼物。
  • Flux则像是一部电视剧,一集一集连续播放,你可以一直看下去,剧集数量不确定,可能有很多集,也可能没有(剧终)。

两者都是Reactor框架中的工具,帮助开发者以非阻塞的方式处理异步数据流,非常适合构建响应式、高性能的应用程序。

5、Mono常用用法

Mono<T>: 表示包含 0个或1个 元素的 异步序列。它用于表示将来某个时刻可能会产生单个值(也可能会产生空值)的异步操作结果。

5.1、创建 Mono

  • Mono.just(T value)创建一个包含单个给定值的 Mono,创建出来的 Mono 序列在发布这些元素之后会自动结束。
  • Mono.empty():创建一个不包含元素,只发布结束消息的 Mono。(在响应式编程中,流的传递是基于元素的,empty 表示没有元素,所以不会进行后续传递,需要使用 switchIfEmpty 进行处理。)
  • Mono.never():empty 里面至少还有一个结束消息,而 never 则是真的啥都没有。
  • Mono.error(Throwable error):创建一个包含给定错误的 Mono。
  • Mono.defer(Callable<Mono<T>> supplier):延迟 Mono 的创建直到订阅时,此时 supplier 函数会被调用实际创建 Mono。示例如下:
Mono.defer(() -> {
    return Mono.error(new RuntimeException);
}).subscribe();
  • Mono.fromCallable(Callable<T> callable):类似于 defer(),但它接受 Callable 并在订阅时执行,返回一个单一结果。使用示例
Mono.fromCallable(() -> "9999").subscribe(System.out::println);
  • Mono.fromCompletionStage(CompletionStage<T> completionStage):从 CompletionStage 创建 Flux。
  • Mono.fromFuture(CompletableFuture<T> future):从 Future 创建 Flux。
  • Mono.fromRunnable(Runnable runnable):从 Runnable 创建 Flux。
  • Mono.fromSupplier(Supplier<T> supplier):从 Supplier 创建 Flux。
  • Mono.justOrEmpty(Optional<? extends T> data):从一个 Optional 对象中创建 Mono。只有 Optional 对象中包含值时,Mono 序列才产生对应的元素。
  • Mono.justOrEmpty(T data):从一个 可能为 null 的对象中创建 Mono。只有对象不为 null 时,Mono 序列才产生对应的元素。
  • Mono.create(Supplier<MonoSink<T>> sinkSupplier):用于手动控制 Mono 流程,通过 MonoSink 提供数据、完成、或抛出错误的能力。使用示例:
Mono.fromSupplier(() -> "Hello").subscribe(System.out::println);
Mono.justOrEmpty(Optional.of("Hello")).subscribe(System.out::println);
Mono.create(sink -> sink.success("Hello")).subscribe(System.out::println);
  • Mono.delay(Deration duration):创建一个 Mono 序列,在指定的延迟时间之后,产生数字 0 作为唯一值。
  • Mono.delayMillis(long duration):同上,指定具体的毫秒数作为延迟时间。

5.2、转换和组合

  • map(Function<T, R> mappper):应用于 Mono 中的数据并映射为新类型。
  • flatMap(Function<T, Mono<R>> mapper):将 Mono 中的值转换为另一个 Mono,并扁平化结果。
  • then(Mono<T> other):当 Mono 完成(成功或失败)时,继续订阅另一个 Mono。
  • zipWith(Mono<?> otherMono, BiFunction<? super T, ?, U> zipper):将两个 Mono 结合在一起,使用 zip 函数合并结果。使用示例:
// 直接拼接 -> [a, c] [b, d]
Flux.just("a", "b")
    .zipWith(Flux.just("c", "d"))
    .subscribe(System.out::println);
// 按照制定规则拼接 -> a-c b-d
Flux.just("a", "b")
    .zipWith(Flux.just("c", "d"), (s1, s2) -> String.format("%s-%s", s1, s2))
    .subscribe(System.out::println);
  • Mono.zip(...Mono sources):合并多个 Mono,当所有 Mono 完成后聚合它们的结果。

5.3、订阅和消费

  • subscribe(Consumer<? super T> consumer):订阅 Mono 并在接收到值时执行 Consumer。
  • subscribe(Consumer<? super T> successHandler, Consumer<Throwable> errorHandler):添加成功和错误处理器。
  • block():阻塞等待 Mono 完成并返回结果。(注意:在异步非阻塞环境下慎用)
  • blockFirst():阻塞调用线程,直到发射出的第一个元素到达,返回返回该元素。如果没有元素发射,则会抛出 NoSuchElementException。(当用于 Mono 上时,效果与 block() 相同)
  • blockLast():阻塞调用线程,直到异步序列完成并返回最后一个元素。如果没有元素发射,则会抛出 NoSuchElementException。(当用于 Mono 上时,效果与 block() 相同)

5.4、注意

在 Project Reactor 中,block()、blockFirst()、blockLast() 这些方法在阻塞期间会 持有主线程,可能导致 线程饥饿、响应延迟 等问题,特别是在处理大量数据或长时间运行的任务时。在大部分情况下,尽量避免在生产环境中使用这些方法,因为它们会破坏反应式编程的优势——非阻塞性 和 背压 支持。在响应式编程中,通常建议使用 subscribe() 方法注册回调函数以非阻塞方式处理异步结果。然而,在测试、初始化或者其他特殊情况中,有时确实需要获取一个同步的结果。

6、Flux 常用方法

Flux<T>: 表示包含 0个或多个 元素的 异步序列。它用于处理连续的数据流,例如集合、数组或事件流。

6.1、创建 Flux

  • Flux.just(T... values):创建一个包含一组值的 Flux,创建出来的 Flux 序列在发布这些元素之后会自动结束。
  • Flux.fromArray(T[] array):从数组创建 Flux。
  • Flux.fromIterable(Iterable<T> iterable):从 Iterable 对象创建 Flux。
  • Flux.fromStream(Stream<T> strean):从 Stream 对象创建 Flux。
  • Flux.empty():创建一个不包含任何元素,只发布结束消息的序列。(在响应式编程中,流的传递是基于元素的,empty 表示没有元素,所以不会进行后续传递,需要使用 switchIfEmpty 进行处理。)
  • Flux.never():创建一个不包含任何元素的序列。
  • Flux.error(Throwable error):创建一个只包含错误消息的序列。
  • Flux.range(int start, int count):创建一个包含从 start 开始计数的 count 个整数的 Flux。使用示例:
Flux.range(1, 10)
    .timeout(Flux.never(), v -> Flux.never()) // 表示永不超时
    .subscribe(System.out::println);
  • Flux.interval(Duration period, Duration delay):创建一个按照固定时间间隔不断产生 Long 类型数值的 Flux 序列。这个序列可以用来做定时任务或者周期性触发某些操作。使用示例:

6.2、转换和过滤

  • map(Function<T, R> mapper):映射每个元素到新的类型。
  • filter(Predicate<T> predicate):根据给定谓词过滤元素。
  • flatMap(Function<T, Publisher<R>> mapper):将每个元素转换为 Publisher 并扁平化结果。
  • concatMap(Function<T, Publisher<R>> mapper):与 flatMap 类似,但保持顺序输出。

6.3、控制流

  • limitRate(int maxConcurrency):限制并发订阅的数量。
  • take(int n):仅取前 n 个元素。
  • skip(int n):跳过前 n 个元素。
  • buffer(int size):按指定大小

6.4、错误处理与完成

  • onErrorResume(BiFunction<Throwable, ? super Flux<T>, ? extends Publisher<U>> resumeFunction):当 Flux 发生错误时,提供备用 Publisher 继续发射元素。
  • doOnError(Consumer<Throwable> errorHandler):添加错误处理器。
  • doOnComplete(Runnable onCompleteHook):在 Flux 完成时执行一个钩子方法。

6.5、订阅和消费

  • subscribe(Consumer<? super T> consumer):订阅 Flux 并在接收到每个元素时执行 Consumer。
  • subscribe(Consumer<? super T> onNext, Consumer<Throwable> onError, Runnable onComplete):添加完整订阅处理器,包括正常接收、错误处理和完成时的回调。
  • block():阻塞等待 Flux 完成并返回结果。(注意:在异步非阻塞环境下慎用)
  • blockFirst():阻塞调用线程,直到发射出的第一个元素到达,返回返回该元素。如果没有元素发射,则会抛出 NoSuchElementException。
  • blockLast():阻塞调用线程,直到异步序列完成并返回最后一个元素。如果没有元素发射,则会抛出 NoSuchElementException。

6.6、注意

在 Project Reactor 中,block()、blockFirst()、blockLast() 这些方法在阻塞期间会 持有主线程,可能导致 线程饥饿、响应延迟 等问题,特别是在处理大量数据或长时间运行的任务时。在大部分情况下,尽量避免在生产环境中使用这些方法,因为它们会破坏反应式编程的优势——非阻塞性 和 背压 支持。在响应式编程中,通常建议使用 subscribe() 方法注册回调函数以非阻塞方式处理异步结果。然而,在测试、初始化或者其他特殊情况中,有时确实需要获取一个同步的结果。

6.7、组合和订阅

  • mergeWith(Flux<? extends T> other):合并两个 Flux 流。
  • reduce(BiFunction<T, T, T> accumulator):对整个序列应用一个累加器函数,返回单一结果。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值