Spring Framework从版本5开始,基于Project Reactor支持响应式编程。

Project Reactor是用于在JVM上构建非阻塞应用程序的Reactive库,基于Reactive Streams规范。

Project Reactor是Spring生态系统中响应式的基础,并且与Spring密切合作进行开发。

Spring WebFlux要求Project Reactor作为核心依赖项。

Project Reactor介绍

Project Reactor主要由下面的模块组成:

  • Reactor Core:包含响应式类型Flux和Mono,它们实现了Reactive Stream的Publisher接口以及一组可应用于这些类型的运算符。
  • Reactor Test:提供一些实用程序来测试响应流。
  • Reactor Extra:提供一些额外的Flux运算符。
  • Reactor Netty:无阻塞且支持背压的TCP,HTTP和UDP的客户端和服务器。
  • Reactor Adapter:用于与其他响应式库(例如RxJava2和Akka Streams)的适配。
  • Reactor Kafka:用于Kafka的响应式API,作为Kafka的生产者和消费者。

要想在应用程序中使用Project Reactor,需要在pom.xml中添加如下maven依赖项:

<dependency>
    <groupId>io.projectreactor</groupId>
    <artifactId>reactor-core</artifactId>
    <version>3.4.0</version>
</dependency>
<dependency>
    <groupId>io.projectreactor</groupId>
    <artifactId>reactor-test</artifactId>
    <version>3.4.0</version>
    <scope>test</scope>
</dependency>

Project Reactor的使用

创建Flux和Mono序列

响应流规范只定义了4个接口,即

  • Publisher
  • Subscriber
  • Subscription
  • Processor

Project Reactor提供了Publisher接口的实现,即Flux和Mono。

Flux定义了一个通用的响应式流,它可以产生零个、一个或多个元素,乃至无限元素。

package com.morris.spring.webflux.projectreactor;

import reactor.core.publisher.Flux;

import java.util.Arrays;

/**
 * Flux的使用
 */
public class FluxDemo {

    public static void main(String[] args) {

        Flux<String> justFlux = Flux.just("hello", "world");
        justFlux.subscribe(System.out::println);

        Flux<String> fromArrayFlux = Flux.fromArray(new String[]{"spring", "webflux"});
        fromArrayFlux.subscribe(System.out::println);

        Flux<Integer> fromIterableFlux = Flux.fromIterable(Arrays.asList(1, 2, 3, 4, 5,
                6, 7, 8));
        fromIterableFlux.subscribe(System.out::println);

        Flux<Integer> rangeFlux = Flux.range(1000, 5);
        rangeFlux.subscribe(System.out::println);

        Flux<String> publish = Flux.just("hello", "world");
        Flux<String> fromFlux = Flux.from(publish);
        fromFlux.subscribe(System.out::println);
    }

}

与Flux相比,Mono类型定义了一个最多可以生成一个元素的流。

package com.morris.spring.webflux.projectreactor;

import reactor.core.publisher.Mono;

import java.util.Optional;

/**
 * Mono的使用
 */
public class MonoDemo {
    public static void main(String[] args) {
        Mono.just("just").subscribe(System.out::println);

        Mono.fromCallable(() -> "fromCallable").subscribe(System.out::println);

        Mono.fromSupplier(() -> "fromSupplier").subscribe(System.out::println);

        Mono.justOrEmpty(null).subscribe(System.out::println);

        Mono.justOrEmpty(Optional.empty()).subscribe(System.out::println);
    }
}

订阅响应式流

Flux和Mono提供了对subscribe()方法的基于lambda的重载,简化了订阅的开发。

subscribe()方法的所有重载都返回Disposable接口的实例,可以用于取消基础的订阅过程。

subscribe()的重载方法:

// 忽略所有事件
subscribe();

// 只处理onNext事件
subscribe(Consumer<T> dataConsumer);

// 处理onNext事件,处理onError事件
subscribe(Consumer<T> dataConsumer, Consumer<Throwable> errorConsumer);

// 处理onNext事件,处理onError事件,处理onComplete事件
subscribe(Consumer<T> dataConsumer, Consumer<Throwable> errorConsumer,
Runnable completeConsumer);

// 处理onNext事件,处理onError事件,处理onComplete事件,可以获得Subscription
subscribe(Consumer<T> dataConsumer, Consumer<Throwable> errorConsumer,
Runnable completeConsumer, Consumer<Subscription>
subscriptionConsumer);

// 走最原始的方式,自己创建Subscriber
subscribe(Subscriber<T> subscriber);

subscribe()的使用:

package com.morris.spring.webflux.projectreactor;

import reactor.core.publisher.Flux;

/**
 * 订阅流
 */
public class SubscribeDemo {
    public static void main(String[] args) {

        Flux<String> justFlux = Flux.just("hello");
        justFlux.subscribe();
        System.out.println("---------");

        Flux<String> justFlux2 = Flux.just("hello");
        justFlux2.subscribe(System.out::println);
        System.out.println("---------");

        Flux.from(subscriber -> {
            for (int i = 0; i < 5; i++) {
                subscriber.onNext(i);
            }
            subscriber.onError(new Exception("异常测试"));
            subscriber.onComplete();
        }).subscribe(
                item -> System.out.println("onNext:" + item),
                ex -> System.out.println("异常情况:" + ex)
        );
        System.out.println("---------");

        Flux.from(subscriber -> {
            for (int i = 0; i < 5; i++) {
                subscriber.onNext(i);
            }
            subscriber.onComplete();
        }).subscribe(
                item -> System.out.println("onNext:" + item),
                ex -> System.out.println("异常情况:" + ex),
                () -> System.out.println("onComplete")

        );
    }
}

操作响应式流

使用响应式流,除了需要能够创建和使用流,还必须能够完美地转换和操作。

转换响应式流:

package com.morris.spring.webflux.projectreactor;

import reactor.core.publisher.Flux;

/**
 * 响应流的操作
 */
public class StreamOperateDemo {
    public static void main(String[] args) {

        // map
        Flux.range(1, 10)
                .map(t -> "hello" + t)
                .subscribe(System.out::println);

        // index
        Flux.range(1, 10)
                .map(t -> "hello" + t)
                .index()
                .subscribe(t -> System.out.println(t.getT1() + ":" + t.getT2()));

        // timestamp
        Flux.range(1, 10)
                .map(t -> "hello" + t)
                .timestamp()
                .subscribe(t -> System.out.println(t.getT1() + ":" + t.getT2()));
    }
}

过滤响应式流:

  1. filter操作符仅传递满足条件的元素。
  2. ignoreElements操作符返回Mono并过滤所有元素。结果序列仅在原始序列结束后结束。
  3. take(n)操作符限制所获取的元素,该方法忽略除前n个元素之外的所有元素。
  4. takeLast仅返回流的最后一个元素。
  5. takeUntil(Predicate)传递一个元素直到满足某个条件。
  6. elementAt(n)只可用于获取序列的第n个元素。

过滤响应式流的使用:

package com.morris.spring.webflux.projectreactor;

import reactor.core.publisher.Flux;

import java.util.Objects;

/**
 * 响应流的过滤
 */
public class StreamFilterDemo {
    public static void main(String[] args) {

        // filter操作符仅传递满足条件的元素
        Flux.range(1, 10).filter(i -> i % 2 == 0).subscribe(System.out::println);

        // ignoreElements操作符返回Mono<T>并过滤所有元素,结果序列仅在原始序列结束后结束
        Flux.range(1, 10).ignoreElements().subscribe();

        // take(n)操作符限制所获取的元素,该方法忽略除前n个元素之外的所有元素。
        Flux.range(1, 10).take(3).subscribe(System.out::println);

        // takeLast(n)仅返回流的最后n个元素。
        Flux.range(1, 10).takeLast(3).subscribe(System.out::println);

        // takeUntil(Predicate)一直传递直到满足某个条件。
        Flux.range(1, 10).takeUntil( t -> Objects.equals(t, 5)).subscribe(System.out::println);

        // elementAt(n)只可用于获取序列的第n个元素。
        Flux.range(1, 10).elementAt(3).subscribe(System.out::println);
    }
}

错误处理

响应式流的语义定义了onError()事件是一个终止操作,该操作之后响应式流会停止执行。

不对异常进行处理:

Flux.from(subscriber -> subscriber.onError(new RuntimeException("error test")))
        .subscribe(System.out::println);

运行结果会抛出如下错误:

13:47:32.620 [main] ERROR reactor.core.publisher.Operators - Operator called default onErrorDropped
reactor.core.Exceptions$ErrorCallbackNotImplemented: java.lang.RuntimeException: error test
Caused by: java.lang.RuntimeException: error test
	at com.morris.spring.webflux.projectreactor.ErrorHandlerDemo.lambda$main$0(ErrorHandlerDemo.java:11)
	at reactor.core.publisher.FluxSource.subscribe(FluxSource.java:66)
	at reactor.core.publisher.Flux.subscribe(Flux.java:8095)
	at reactor.core.publisher.Flux.subscribeWith(Flux.java:8268)
	at reactor.core.publisher.Flux.subscribe(Flux.java:8065)
	at reactor.core.publisher.Flux.subscribe(Flux.java:7989)
	at reactor.core.publisher.Flux.subscribe(Flux.java:7932)
	at com.morris.spring.webflux.projectreactor.ErrorHandlerDemo.main(ErrorHandlerDemo.java:12)

可以通过以下方式进行错误处理:

  1. 在subscribe()中指定onError事件的处理逻辑
  2. 通过onErrorReturn()指定发生异常后,返回固定值
  3. 通过onErrorResume()指定异常发生后执行备用工作流
  4. 通过onErrorMap()捕获异常并将其转换为另一个异常
  5. 定义一个在发生错误时重新执行的响应式工作流。如果源响应序列发出错误信号,那么retry()会重新订阅该序列。

错误处理的使用:

package com.morris.spring.webflux.projectreactor;

import reactor.core.publisher.Flux;

/**
 * 异常处理
 */
public class ErrorHandlerDemo {
    public static void main(String[] args) {

        // 不对异常进行处理
        Flux.from(subscriber -> subscriber.onError(new RuntimeException("error test")))
                .subscribe(System.out::println);

        // 有异常返回一个固定值
        Flux.just(10).map(t -> t / 0).onErrorReturn(0).subscribe(System.out::println);

        // 有异常返回一个备用流
        Flux.just(10).map(t -> t / 0).onErrorResume(e -> Flux.just(0)).subscribe(System.out::println);

        // 有异常可将异常封装为一个新的异常
        Flux.just(10).map(t -> t / 0).onErrorMap(e -> new RuntimeException(e.getMessage())).subscribe(System.out::println);
    }
}