springboot学习24

一、响应式流接口定义:
Publisher,Subscriber,Subscription 和 Processor。
Publisher 接口声明了一个 subscribe() 方法:

public interface Publisher<T> {
    
    
    void subscribe(Subscriber<? super T> subscriber);
}

Subscriber 如果订阅了,就可以从 Publisher 中接收消息。
Subscriber:

public interface Subscriber<T> {
    
    
 	// Subscriber 通过调用 onSubscribe() 函数将会收到第一个消息。
 	// 它通过一个 Subscription 对象将消息传输给 Subscriber
    void onSubscribe(Subscription s);
    // 每一个 Publisher 发布的项目都会通过调用 onNext() 方法,将数据传输到 Subscriber。
    void onNext(T t);
    // 如果出现错误,onError() 方法将被调用
    void onError(Throwable t);
    // 如果 Publisher 没有更多的数据需要发送了,同时也不会再生产任何数据了,
    // 将会调用 onComplete() 
    void onComplete();
}

Subscription:

public interface Subscription {
    
    
   //去请求被被发送了的数据,long 值的参数来表示它将会接收多少数据
    void request(long n);
    // 取消订阅
    void cancel();
}

Processor接口:

// Processor 将会接收数据然后以一定的方式处理这些数据。
// 然后变为一个 Publisher,将处理的结果发布到 Subscriber。
public interface Processor<T, R> extends Subscriber<T>, Publisher<R> {
    
    }

二、、reactor
Reactor 的两个核心类型:Mono,Flux。

Flux 表示零个、一个或多个(可能是无限个)数据项的管道
Mono 特定用于已知的数据返回项不多于一个的响应式类型
// Mono 特定用于已知的数据返回项不多于一个的响应式类型
Mono.just("Craig")
    .map(n -> n.toUpperCase())
    .map(cn -> "Hello, " + cn + "!")
    .subscribe(System.out::println);

1)添加依赖
在pom添加Reactor依赖:

<dependency>
	<!-- 在springboot中没有必要指定依赖<version> -->
    <groupId>io.projectreactor</groupId>
    <artifactId>reactor-core</artifactId>
</dependency>

测试依赖pom:

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

非springboot项目添加依赖,需要在构建中增加 Reactor 的 Bismuth-RELEASE 。

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>io.projectreactor</groupId>
            <artifactId>reactor-bom</artifactId>
            <version>Bismuth-RELEASE</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

2)响应式操作

创建操作
联合操作
传输操作
逻辑处理操作

下面一些测试代码,在pom先引入依赖:

 <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-test</artifactId>
            <scope>test</scope>
</dependency>

1.从对象进行创建


@SpringBootTest
public class ReactorTest {
    
    
	// 但它没有订阅者。要是没有订阅者,数据不会流动。
	@Test
	public void createAFlux_just() {
    
    
	//创建了一个 Flux
	   Flux<String> fruitFlux = Flux
	       .just("Apple", "Watermelon", "Banana", "Orange");
	}
}

想要流动,可以添加订阅者

	
    @Test
    public void createAFlux_just() {
    
    
        Flux<String> flux = Flux
                .just("Apple", "Watermelon", "Banana", "Orange");
		// 添加一个订阅者,可以调用 Flux 中的 subscribe() 方法:
        flux.subscribe(
                f -> System.out.println("fruits: " + f)
        );
    }

使用 StepVerifier 编写测试用例:

@SpringBootTest
public class ReactorTest {
    
    
    @Test
    public void createAFlux_just() {
    
    
        Flux<String> flux = Flux
                .just("Apple", "Watermelon", "Banana", "Orange");

//        flux.subscribe(
//                f -> System.out.println("fruits: " + f)
//        );

        StepVerifier.create(flux)
                .expectNext("Apple")
                .expectNext("Watermelon")
                .expectNext("Banana")
                .expectNext("Orange")
                .verifyComplete();
    }
}

2.从集合创建
从数组创建一个 Flux:

@SpringBootTest
public class ReactorTest {
    
    
 	@Test
    public void createAFlux_fromArray() {
    
    
	    // 数组创建一个 Flux
        String[] fruits = new String[] {
    
    
                "Apple", "Watermelon", "Banana", "Orange"};

        Flux<String> fFlux = Flux.fromArray(fruits);
        StepVerifier.create(flux)
                .expectNext("Apple")
                .expectNext("Watermelon")
                .expectNext("Banana")
                .expectNext("Orange")
                .verifyComplete();
    }
}

从java.util.List、java.util.Set 或任何实现了 java.lang.Iterable 接口的类作为Flux源:

@SpringBootTest
public class ReactorTest {
    
    
    @Test
    public void createAFlux_fromIterable() {
    
    
    	//  java.util.List、java.util.Set 或任何实现了 java.lang.Iterable 接口的类
        List<String> fruitList = new ArrayList<>();
        fruitList.add("Apple");
        fruitList.add("Watermelon");
        fruitList.add("Banana");
        fruitList.add("Orange");
        Flux<String> flux = Flux.fromIterable(fruitList);
        
        StepVerifier.create(flux)
                .expectNext("Apple")
                .expectNext("Watermelon")
                .expectNext("Banana")
                .expectNext("Orange")
                .verifyComplete();
    }
}

Java Stream 作为 Flux 源:

@SpringBootTest
public class ReactorTest {
    
    
	 @Test
    public void createAFlux_fromStream() {
    
    
        Stream<String> fruitStream =
                Stream.of("Apple", "Watermelon", "Banana", "Orange");
        Flux<String> flux = Flux.fromStream(fruitStream);
        // ... verify steps
        StepVerifier.create(flux)
                .expectNext("Apple")
                .expectNext("Watermelon")
                .expectNext("Banana")
                .expectNext("Orange")
                .verifyComplete();
    }
}

3.生成 Flux 数据
创建一个范围的 Flux:

@SpringBootTest
public class ReactorTest {
    
    
	
    @Test
    public void createAFlux_range() {
    
    
	    //创建的范围 Flux 的起始值为 1,结束值为 5
        Flux<Integer> flux = Flux.range(1, 5);
        StepVerifier.create(flux)
                .expectNext(1)
                .expectNext(2)
                .expectNext(3)
                .expectNext(4)
                .expectNext(5)
                .verifyComplete();
    }
}

静态的 interval() 方法来创建每秒发送一个值的 Flux:

@SpringBootTest
public class ReactorTest {
    
    
	@Test
    public void createAFlux_interval() {
    
    
    	// 间隔 Flux 发出的值以 0 开始,并在每个连续项上递增
        Flux<Long> flux = Flux.interval(Duration.ofSeconds(1)).take(5);
        StepVerifier.create(flux)
                .expectNext(0L)
                .expectNext(1L)
                .expectNext(2L)
                .expectNext(3L)
                .expectNext(4L)
                .verifyComplete();
    }
}

三、响应式类型结合
1、合并响应式类型
如果两个 Flux 流要建立一个汇聚结果的 Flux,可以使用mergeWith()。

@SpringBootTest
public class ReactorTest {
    
    
	@Test
    public void mergeFluxes() {
    
    
        Flux<String> flux1 = Flux
                .just("a", "b", "c")
                // 通常情况下,Flux 会尽可能快的快地发送数据.
                // 使用delayElements() 操作,用来将数据发送速度减慢 —— 每 0.5s 发送一个数据。
                .delayElements(Duration.ofMillis(500));

        Flux<String> flux2 = Flux
                .just("d", "e", "f")
                // 在延迟 250ms 后才会发送数据
                .delaySubscription(Duration.ofMillis(250))
                .delayElements(Duration.ofMillis(500));
		// mergeWith() 不能保证源之间的完美交替
        Flux<String> mergedFlux = flux1.mergeWith(flux2);
		// 验证
        StepVerifier.create(mergedFlux)
                .expectNext("a")
                .expectNext("d")
                .expectNext("b")
                .expectNext("e")
                .expectNext("c")
                .expectNext("f")
                .verifyComplete();
    }
}

把 flux1 和 flux2 压缩在一起:

@SpringBootTest
public class ReactorTest {
    
    
	@Test
    public void zipFluxes() {
    
    
        Flux<String> flux1 = Flux.just("a", "b", "c");
        Flux<String> flux2 = Flux.just("d", "e", "f");
		// 与 mergeWith() 不同的是,zip() 操作是一个静态的创建操作。
		// 从压缩后的 Flux 发送出来的每个项目都是 Tuple2(包含两个对象的容器)。
        Flux<Tuple2<String, String>> zippedFlux = Flux.zip(flux1, flux2);

        StepVerifier.create(zippedFlux)
                .expectNextMatches(p ->
                        p.getT1().equals("a") &&
                                p.getT2().equals("d"))
                .expectNextMatches(p ->
                        p.getT1().equals("b") &&
                                p.getT2().equals("e"))
                .expectNextMatches(p ->
                        p.getT1().equals("c") &&
                                p.getT2().equals("f"))
                .verifyComplete();
    }
}

何压缩的 flux1 和 flux2,产生 String 类型的的 Flux 对象:

@SpringBootTest
public class ReactorTest {
    
    
	 @Test
    public void zipFluxesToObject() {
    
    
        Flux<String> flux1 = Flux.just("a", "b", "c");
        Flux<String> flux2 = Flux.just("d", "e", "f");

		// 给 zip() 的 Function 接口(这里给出一个 lambda 表达式)把两个值连接,由压缩后的 Flux 进行数据发送
        Flux<String> zippedFlux = Flux.zip(flux1, flux2,
                (c, f) -> c + " ---> " + f);

        StepVerifier.create(zippedFlux)
                .expectNext("a ---> d")
                .expectNext("b ---> e")
                .expectNext("c ---> f")
                .verifyComplete();
    }
}

2、选择第一个响应式类型进行发布

@SpringBootTest
public class ReactorTest {
    
    

	@Test
    public void firstFlux() {
    
    
    	// 因为在flux2 已经开始发布后 100ms,flux1 才开始发布数据。
    	// 所以新创建的 Flux 将完全忽略flux1,而只发布flux2中的数据
        Flux<String> flux1 = Flux.just("a", "b", "c")
                .delaySubscription(Duration.ofMillis(100));
        Flux<String> flux2 = Flux.just("d", "e", "f");
		
		// 通过使用 first(),它创建了一个新的 Flux,
		// 只发布从第一个源 flux2 发布的数据
        Flux<String> firstFlux = Flux.first(flux1, flux2);

        StepVerifier.create(firstFlux)
                .expectNext("d")
                .expectNext("e")
                .expectNext("f")
                .verifyComplete();
    }
}

四、转换和过滤响应式流
1、从响应式类型中过滤数据
跳过指定数量的项:


// 跳过指定数量的项
@Test
public void skipSome() {
    
    
 //  skip(3) 将生成一个新的流,该流跳过前三个项,并且只发布最后两个项
    Flux<String> flux1 = Flux.just(
            "a", "b", "c", "d", "e")
            .skip(3);

    StepVerifier.create(flux1)
            .expectNext("d", "e")
            .verifyComplete();
}

过一段时间再跳过前几个项目(skip):

@Test
public void skipSeconds() {
    
    
    // 在发出任何值之前等待 4 秒的 Flux,项之间具有 1 秒延迟
    Flux<String> flux1 = Flux.just(
            "a", "b", "c", "d", "e")
            .delayElements(Duration.ofSeconds(1))
            .skip(Duration.ofSeconds(4));	// skip() 跳过前几个项

    StepVerifier.create(flux1)
            .expectNext("d", "e")
            .verifyComplete();
}

只发出前几个项(take):

@Test
public void take() {
    
    
    Flux<String> flux1 = Flux.just(
            "a", "b", "c", "d", "e")
            .take(3);//take() 只发出前几个项

    StepVerifier.create(flux1)
            .expectNext("a", "b", "c")
            .verifyComplete();
}

使用 take() 的在订阅后的前 n(这里是3.5秒) 秒内发出尽可能多的项:

@Test
public void take2() {
    
    
    Flux<String> flux1 = Flux.just(
            "a", "b", "c", "d", "e")
            .delayElements(Duration.ofSeconds(1))
            .take(Duration.ofMillis(3500));

    StepVerifier.create(flux1)
            .expectNext("a", "b", "c")
            .verifyComplete();
}
skip() 和 take() 操作可以看作是基于计数或持续时间的筛选条件的操作。

还有个filter():
给定一个决定一个项是否通过 Flux 的 Predicate,filter() 操作允许你根据需要的任何条件有选择地发布。

@Test
public void filter() {
    
    
 // filter() 被赋予一个 Predicate。
 // 只接收没有空格的String。
    Flux<String> flux1 = Flux.just(
            "a", "b", "c", "d", "hello world", "e")
            .filter(np -> !np.contains(" "));

    StepVerifier.create(flux1)
            .expectNext("a", "b", "c", "d", "e")
            .verifyComplete();
}

过滤的是已经收到的任何项目,如只有唯一的 String 值将从 Flux 中发出:

@Test
public void distinct() {
    
    
	// 尽管 “a” 从源 Flux 中发布两次,但在 distinct Flux 中只发布一次	  
    Flux<String> flux1 = Flux.just(
            "a", "b", "c", "a", "e", "d")
            .distinct(); // 只有唯一的 String 值将从 Flux

    StepVerifier.create(flux1)
            .expectNext("a", "b", "c", "e", "d")
            .verifyComplete();
}

2、映射响应式数据
用户的 String 值的 Flux 映射到 User 对象的新 Flux,User为自定义的用户类。

// map() 映射是同步执行的。 因为每个项都是由源 Flux 发布的.
// 如果要异步执行映射,应使用 flatMap() 操作
	@Test
   public void mapdemo() {
    
    
  		// 用 just() 创建的流携带的是 String 对象
  		// 但由 map() 生成的流携带的是 User 对象
       Flux<User> flux1 = Flux
               .just("zhang san", "wang wu", "zhao liu")
               .map(n -> {
    
    
                   String[] split = n.split("\\s");
                   return new User(split[0], split[1]);
               });

       StepVerifier.create(flux1)
               .expectNext(new User("zhang", "san"))
               .expectNext(new User("wang", "wu"))
               .expectNext(new User("zhao", "liu"))
               .verifyComplete();
   }

flatMap与subscribeOn方法一起使用:(可以通过将工作分成多个并行线程来增加流的吞吐量)

@Test
public void flatMapDemo() {
    
    
    Flux<User> flux1 = Flux
            .just("zhang san", "wang wu", "zhao liu")
            .flatMap(n -> Mono.just(n).map(p -> {
    
     	// 将传入 String 转换为 String 类型的 Mono
                        String[] split = p.split("\\s");
                        return new User(split[0], split[1]); // 将 String 转换为 User
                    })
            .subscribeOn(Schedulers.parallel()));// subscribeOn表示每个订阅应该在一个并行线程中进行。

    List<User> userList = Arrays.asList(
            new User("zhang", "san"),
            new User("wang", "wu"),
            new User("zhao", "liu"));

	//因为是并行完成的,所以无法保证先完成哪项,无法知道产生的 Flux 中排放的项目的顺序。
	// StepVerifier 只能验证发出的每个项是否存在于 User 对象的预期列表中,
	//并且在 Flux 完成之前将有三个这样的项
    StepVerifier.create(flux1)
            .expectNextMatches(p -> userList.contains(p))
            .expectNextMatches(p -> userList.contains(p))
            .expectNextMatches(p -> userList.contains(p))
            .verifyComplete();
}

调度程序支持多个并发模型:

Schedulers 方法 描述
.immediate() 在当前线程中执行订阅
.single() 在单个可重用线程中执行订阅,对所有调用方重复使用同一线程
.newSingle() 在每个调用专用线程中执行订阅
.elastic() 在从无限弹性池中提取的工作进程中执行订阅,根据需要创建新的工作线程,并释放空闲的工作线程(默认情况下 60 秒)
.parallel() 在从固定大小的池中提取的工作进程中执行订阅,该池的大小取决于 CPU 核心的数量。

3、在响应式流上缓冲数据

@Test
public void buffer() {
    
    
	// 创建一个新的 List 集合的 Flux,其中每个 List 的元素数不超过指定的数目
    Flux<String> flux1 = Flux.just(
            "a", "b", "c", "d", "e");

	// String 元素的 Flux 被缓冲到一个 List 集合的新 Flux 中,
	// 每个 List 集合包含的项不超过三个。
    Flux<List<String>> bufferedFlux = flux1.buffer(3);

    StepVerifier
            .create(bufferedFlux)
            .expectNext(Arrays.asList("a", "b", "c"))
            .expectNext(Arrays.asList("d", "e"))
            .verifyComplete();
}

buffer() 与 flatMap() 结合使用,可以并行处理每个 List 集合:

@Test
public void bufferAndFlatMap() {
    
    
    Flux.just(
            "a", "b", "c", "d", "e")
            .buffer(3)
            .flatMap(x ->
                    Flux.fromIterable(x)
                            .map(y -> y.toUpperCase())
                            .subscribeOn(Schedulers.parallel())
                            .log()
            ).subscribe();
}

collectList() 产生一个 Mono:

@Test
 public void collectListDemo() {
    
    
     Flux<String> flux = Flux.just(
             "a", "b", "c", "d", "e");
	 //collectList() 生成一个发布 List 的 Mono,而不是生成一个发布 List 的 Mono
     Mono<List<String>> mono = flux.collectList();

     StepVerifier
             .create(mono)
             .expectNext(Arrays.asList(
                     "a", "b", "c", "d", "e"))
             .verifyComplete();
 }

键又第一个字母确定,经流的最后一个条目将覆盖所有先前的条目。

@Test
public void collectMap() {
    
    
    Flux<String> flux = Flux.just(
            "a2", "b2", "k2", "b1", "a5", "p3");
    Mono<Map<Character, String>> mono =
            flux.collectMap(a -> a.charAt(0));


    mono.subscribe(
            f -> System.out.println("fruits: " + f)
    );

    StepVerifier
            .create(mono)
            .expectNextMatches(map -> {
    
    
                return
                        map.size() == 4 &&
                                map.get('a').equals("a5") &&
                                map.get('k').equals("k2") &&
                                map.get('b').equals("b1") &&
                                map.get('p').equals("p3");
            })
            .verifyComplete();
}

五、逻辑操作
有时只要是否符合某些条件,all()any() 操作可以执行这样的逻辑。
all():

@Test
public void allDemo() {
    
    
    Flux<String> flux = Flux.just(
            "ab", "asd", "aqwer", "abcd");

	// 所有的条目都包含字母 a,false
    Mono<Boolean> hasAMono = flux.all(a -> a.contains("a"));
    StepVerifier.create(hasAMono)
            .expectNext(true)
            .verifyComplete();
	
	// 所有的条目都包含字母 b,false
    Mono<Boolean> hasBMono = flux.all(a -> a.contains("b"));
    StepVerifier.create(hasBMono)
            .expectNext(false)
            .verifyComplete();
}

至少有一个条目匹配:
any():

@Test
public void any() {
    
    
    Flux<String> flux = Flux.just(
            "ab", "asd", "aqwer", "abcd");

	// 至少有一个条目包含a, true
    Mono<Boolean> hasSMono = flux.any(a -> a.contains("s"));
    StepVerifier.create(hasSMono)
            .expectNext(true)
            .verifyComplete();
	// 至少有一个条目包含n,false
    Mono<Boolean> hasNMono = flux.any(a -> a.contains("n"));
    StepVerifier.create(hasNMono)
            .expectNext(false)
            .verifyComplete();
}

猜你喜欢

转载自blog.csdn.net/tongwudi5093/article/details/113838290