[In-depth analysis of spring cloud gateway] 08 Reactor knowledge literacy

1. Overview of reactive programming

1.1 Background knowledge

In order to cope with high-concurrency server-side development scenarios, in 2009, Microsoft proposed a more elegant way to implement asynchronous programming - Reactive Programming, which we call reactive programming. Subsequently, Netflix and LightBend provided technologies such as RxJava and Akka Stream, which made the Java platform also have a framework for implementing responsive programming.
On September 28, 2017, Spring 5 was officially released. The biggest significance of the release of Spring 5 is that it has taken a big step forward in the popularization of responsive programming technology. At the same time, Spring Reactor, the framework that supports Spring 5 responsive programming behind the scenes, has also entered the milestone version 3.1.0.

1.2 What is reactive programming

Reactive programming is a programming paradigm oriented towards data flow and change propagation. This means that static or dynamic data flows can be easily expressed in programming languages, and the associated computing model will automatically propagate changing values ​​through the data flow.
Reactive programming is based on the idea of ​​reactor (Reactor is a reactive framework running on Java8). When you do an io operation with a certain delay before it can be returned, it will not block, but will return a stream immediately. And subscribe to this stream, when the return data is generated on this stream, you can be notified immediately and call the callback function to process the data.
Spreadsheet programs are an example of reactive programming. Cells can contain literal values ​​or formulas like "=B1+C1", and the value of the cell containing the formula will change based on the changes in the values ​​of other cells.
One of the core features of responsive communication: Change propagation: After a cell changes, it will cause corresponding changes in other cells that directly and indirectly reference it, like a domino.

1.3 Implementation based on Reactor

Reactor is a responsive framework that runs on Java8 and meets the Reactice specification. It provides a set of responsive APIs.
Reactor has two core classes: Flux and Mono, both of which implement the Publisher interface.
Flux is similar to RxJava's Observable, which can trigger zero to many events and end processing or trigger errors according to the actual situation.
Mono can only fire at most one event, so you can use Mono to notify when an asynchronous task completes.
Both Flux and Mono are publishers of data streams. Both Flux and Mono can send out three data signals: element value, error signal, and completion signal. Both error signal and completion signal represent termination signals, and the termination signal is used to tell subscribers the data. The end of the stream, the error signal terminates the data stream and passes the error message to the subscriber.
Characteristics of the three signals:
error signal and completion signal are both termination signals and cannot coexist.
If no element value is sent, but an error or completion signal is sent directly, it means that the data stream is empty.
If there is no error signal and no completion signal, it means is an unlimited data stream

The Mono schematic diagram is as follows:
Insert image description here

The Flux schematic is as follows:
Insert image description here

Combining the above two figures, it is found that Mono and Flux are very similar. It's just that Mono only receives one element, while Flux receives multiple elements

2. Sample code

2.1 Mono

package com.reactor.demo;

import lombok.extern.slf4j.Slf4j;
import org.junit.Test;
import reactor.core.publisher.Mono;

import java.time.Duration;
import java.util.Optional;
import java.util.function.Consumer;
import java.util.function.Function;

@Slf4j
public class MonoTest {
    
    
    @Test
    public void test1() {
    
    
        //just用法
        Mono.just("hello world").subscribe(System.out::println);
        //runnable创建mono
        Mono<Void> sinkMono = Mono.fromRunnable(() -> System.out.println("runnable"));
        //这句不会输出
        sinkMono.doOnNext(unused -> System.out.println("void success"));
        //这句也不会输出
        sinkMono.subscribe(o -> System.out.println("void result" + o));

        //创建一个不包含任何元素,只发布结束消息的序列。,这里的hello empty是不会输出的。
        Mono.empty()
                //输出“empty的入参是null”
                .doOnSuccess(o -> System.out.println("empty的入参是" + o))
                //这句不会输出
                .subscribe(o -> System.out.println("hello empty"));
        //empty里面至少还有一个结束消息,而never则是真的啥都没有。"never的入参是"不会输出 ,这里的hello never也不会输出
        Mono.never().doOnSuccess(o -> System.out.println("never的入参是" + o)).subscribe(o -> System.out.println("hello never"));
    }

    @Test
    public void test2() {
    
    
        //传入supplier
        Mono.fromSupplier(() -> "Hello supplier").subscribe(System.out::println);
        //传入optional
        Mono.justOrEmpty(Optional.of("Hello optional")).subscribe(System.out::println);
        //通过sink来创建一个正常执行的Mono
        Mono.create(sink -> sink.success("Hello sink")).subscribe(System.out::println);
        //通过sink来创建一个抛出异常的Mono
        Mono.create(sink -> sink.error(new RuntimeException("sink error"))).subscribe(System.out::println);
        //defer的入参实际上是一个Mono工厂
        Mono.defer(() -> Mono.just("hello defer")).subscribe(System.out::println);
    }

    @Test
    public void test3() {
    
    
        //callable,有返回值
        Mono.fromCallable(() -> "callable").subscribe(System.out::println);
        //runnable无返回值
        Mono<Void> mono = Mono.fromRunnable(() -> System.out.println("run"));
        //下面的hello runnable是不会输出的。因为subscribe一个Mono<Void>,不会产生任何结果
        mono.subscribe(o -> System.out.println("hello runnable"));
    }

    @Test
    public void test4() {
    
    
        //延迟3秒输出
        Mono.delay(Duration.ofSeconds(3)).doOnNext(new Consumer<Long>() {
    
    
            @Override
            public void accept(Long aLong) {
    
    
                System.out.println(aLong);
            }
        }).block();

    }

    @Test
    public void test5() {
    
    
        //直接输出了异常
        Mono.error(new RuntimeException("这是一个异常")).subscribe(new Consumer<Object>() {
    
    
            @Override
            public void accept(Object o) {
    
    
                System.out.println("error:" + o);
            }
        });

        Mono.defer(() -> {
    
    
            return Mono.error(new RuntimeException("这是第二个异常"));
        }).subscribe(new Consumer<Object>() {
    
    
            @Override
            public void accept(Object o) {
    
    
                System.out.println("defer error:" + o);
            }
        });
    }

    @Test
    public void test6() {
    
    
        //通过map可以对元素进行转换
        Mono.just("just one").map(new Function<String, Integer>() {
    
    
            @Override
            public Integer apply(String s) {
    
    
                return 1;
            }
        }).doOnNext(new Consumer<Integer>() {
    
    
            @Override
            public void accept(Integer integer) {
    
    
                System.out.println("转换后的结果:" + integer);
            }
        }).subscribe();
    }
}

2.1 Flux

package com.reactor.demo;

import org.junit.Test;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.time.Duration;
import java.util.Arrays;
import java.util.function.Consumer;

public class FluxTest {
    
    

    /**
     * 基本用法
     */
    @Test
    public void test1() {
    
    
        //通过just传入可变的参数,依次输出
        Flux.just("hello", "world", "just").doOnNext(System.out::println)
                .doOnComplete(() -> System.out.println("just over")).subscribe();
        //传入一个范围
        Flux.range(100, 10)
                .doOnNext(System.out::println).doOnComplete(() -> System.out.println("OK")).subscribe();
        //传入list
        Flux.fromIterable(Arrays.asList("01", "02", "03")).doOnNext(System.out::println).subscribe();
        //传入一个数组
        Flux.fromArray(new Object[]{
    
    "obj1", "obj2"}).doOnNext(System.out::println).subscribe();
    }


    /**
     * 处理空值
     */
    @Test
    public void testEmpty() {
    
    
        //如果序列是个空的,就给个默认值
        Flux.empty().defaultIfEmpty(1).doOnNext(System.out::println).subscribe();
        //如果序列是空的,就用新序列代替
        Flux.empty().switchIfEmpty(Mono.just("100")).doOnNext(System.out::println).subscribe();
    }


    /**
     * 序列在执行时的一些监听方法doOnXXXX
     */
    @Test
    public void testDoOn() {
    
    
        System.out.println("----------");
        Flux.range(100, 10)
                .doOnNext(System.out::println).doOnComplete(() -> System.out.println("OK"));

        System.out.println("----------");
        Flux.range(100, 10).doFirst(() -> System.out.println("第一个执行开始")).subscribe();

        System.out.println("----------");
        Flux.range(100, 10).doFinally(it -> System.out.println("终止信号的类型为" + it.name())).subscribe();

        System.out.println("----------");
        Flux.range(100, 10).doOnSubscribe(it -> System.out.println("该序列已被订阅")).subscribe();

        System.out.println("----------");

        Flux.range(100, 10).doOnRequest(value -> System.out.println("doOnRequest:" + value)).subscribe();

        //在完成或者error时,也就是序列终止时执行runnable
        System.out.println("----------");
        Flux.range(100, 10).doOnTerminate(() -> System.out.println("doOnTerminate")).subscribe();

        //doOnEach每次向下游传播,都会得到一个信号类型,可以根据该信号类型执行一些操作
        System.out.println("----------");
        Flux.range(100, 10).doOnEach(it -> System.out.println("doOnEach:" + it)).subscribe();
    }


    /**
     * filter用法
     */
    @Test
    public void testFilter() {
    
    

        System.out.println("----------");
        //将上游的数据进行类型判断,符合该类型的数据将流向下游
        Flux.just(new Object(), "Hello", 1)
                .ofType(String.class).doOnNext(System.out::println)
                .doOnComplete(() -> System.out.println("过滤String示例")).subscribe();

        System.out.println("----------");
        //过滤数据
        Flux.range(100, 10)
                .filter(it -> it > 105)
                .doOnComplete(() -> System.out.println("取出大于105示例")).subscribe();

        System.out.println("----------");
        //将重复数据过滤,重复数据在整个序列中只保留一个
        Flux.range(100, 10)
                .concatWith(Flux.just(100, 100, 100))
                .distinct().doOnNext(System.out::println)
                .doOnComplete(() -> System.out.println("去除重复数字示例")).subscribe();

        System.out.println("----------");

        //将后来的重复数据过滤,如下,第二个flux拼接到第一个序列时,只会把第二个元素本身的重复元素过滤
        Flux.range(100, 10)
                .concatWith(Flux.just(100, 100, 100))
                .distinctUntilChanged().doOnNext(System.out::println)
                .doOnComplete(() -> System.out.println("将后来的重复数据过滤")).subscribe();

        System.out.println("----------");
        //在序列的开始获取5个元素,
        // limitRequest为true时,则不管该序列会发射多少元素,该参数会向上传递背压,则上游序列只会发出设定的5个元素
        //为false时,则不控制上有元素可以发出N个元素
        Flux.range(100, 10).take(5, false)
                .doOnComplete(() -> System.out.println("在序列的开始获取5个元素")).subscribe();

        System.out.println("----------");
        //参数为时间单位,意味着take获取元素,只会在该时间限制内获取。
        Flux.range(100, 10).take(Duration.ofSeconds(10))
                .doOnNext(new Consumer<Integer>() {
    
    
                    @Override
                    public void accept(Integer integer) {
    
    
                        System.out.println("当前时间戳为:" + System.currentTimeMillis() + ",数字为:" + integer);
                    }
                })
                .doOnComplete(() -> System.out.println("在指定时间内获取元素"))
                .subscribe(System.out::println);

        System.out.println("----------");
        //获取最后的N位元素
        Flux.range(100, 10).takeLast(2)
                .doOnComplete(() -> System.out.println("获取最后的2位元素"))
                .subscribe(System.out::println);

        System.out.println("----------");
        //获取元素,知道符合条件后停止向下游发送数据,包括条件本身,也就是当it>105的元素也会被发布至下游
        Flux.range(100, 10).takeUntil(it -> it > 105)
                .doOnComplete(() -> System.out.println("一直取数,直到大于105结束"))
                .subscribe(System.out::println);

        System.out.println("----------");
        //获取元素,当元素符合该断言时,如果不符合直接终止,不包含条件本身
        Flux.range(100, 10).takeWhile(it -> it < 105)
                .doOnComplete(() -> System.out.println("取出小于105示例"))
                .subscribe(System.out::println);

        System.out.println("----------");
        //获取指定某个位置的一个元素
        Flux.range(100, 10).elementAt(0)
                .doOnSuccess(new Consumer<Integer>() {
    
    
                    @Override
                    public void accept(Integer i) {
    
    
                        System.out.println("获取指定某个位置的一个元素:" + i);
                    }
                })
                .subscribe();

        System.out.println("----------");
        //获取最后一个元素,last()如果为空则抛出异常,last(1)如果为空则发出默认值
        Flux.range(100, 10)
                .takeWhile(it -> it > 105).last(1)
                .subscribe(System.out::println);

        System.out.println("----------");
        //跳至第几秒开始执行
        Flux.range(100, 10)
                .skip(Duration.ofSeconds(5)).subscribe(System.out::println);

        System.out.println("----------");
        //跳至第几个元素开始执行
        Flux.range(100, 10)
                .skip(5).subscribe(System.out::println);

        System.out.println("----------");
        //从开始跳到最后第N个元素结束
        Flux.range(100, 10).skipLast(5).subscribe(System.out::println);

        System.out.println("----------");
        //跳至满足条件的地方开始执行,从第一个元素开始,知道满足条件,开始发送至下游
        Flux.range(100, 10).skipUntil(it -> it > 105).subscribe(System.out::println);

        System.out.println("----------");
        //每隔一段时间抽取样本数(取在这个时间的最后一个元素),如果相隔实现大于序列的执行时间,则去最后一元素
        Flux.range(100, 100000000).sample(Duration.ofMillis(100)).subscribe(System.out::println);

        System.out.println("----------");
        //每隔一段时间抽取样本数(取在这个时间的第一个元素),如果相隔实现大于序列的执行时间,则取第一个元素
        Flux.range(100, 10).sampleFirst(Duration.ofMillis(100)).subscribe(System.out::println);

        System.out.println("----------");
        //只获取一个元素,single()如果为空或者超多一个,抛出异常,single(1)如果为空返回默认值,如果多个抛出异常,singleOrEmpty()可以允许为空
        Flux.range(100, 10).single(1).subscribe(System.out::println);
    }


    /**
     * 当被订阅后如果发生异常,则stream会停止运行
     * 此时可以通过处理error来决定如何处理异常
     * 可以将异常跳过、将异常替换等
     */
    @Test
    public void testErrorHandle() {
    
    
        System.out.println("----------");
        Flux.just(1, 2, 3, 0, 5, 4).map(it -> {
    
    
                    it = 100 / it;
                    return it;
                })
                //报错后返回,并停止运行
                .onErrorResume(e -> {
    
    
                    return Mono.just(10000);
                })
                .doFinally(type -> {
    
    
                    System.out.println(type);
                })
                .subscribe(System.out::println);

        System.out.println("----------");
        Flux.just(1, 2, 3).doOnNext(new Consumer<Integer>() {
    
    
            @Override
            public void accept(Integer integer) {
    
    
                System.out.println(integer);
                if (integer == 2) {
    
    
                    throw new RuntimeException("触发异常");
                }
            }
        }).doOnError(new Consumer<Throwable>() {
    
    
            @Override
            public void accept(Throwable throwable) {
    
    
                System.out.println("doOnError:" + throwable.getMessage());
            }
        }).subscribe();


        System.out.println("----------");
        Flux.just(1, 2, 3, 0, 5, 4).map(it -> {
    
    
                    it = 100 / it;
                    return it;
                })
                //报错后继续运行,并执行相关操作
                .onErrorContinue((e, it) -> {
    
    
                    System.out.println(e.getMessage());
                })
                .doFinally(type -> {
    
    
                    System.out.println(type);
                })
                .subscribe(System.out::println);
    }

    @Test
    public void flatMapTest() {
    
    
        //输出50,100
        Flux.just(5, 10).flatMap(x -> Flux.just(x * 10)).toStream().forEach(System.out::println);


    }
}

Reference article

Guess you like

Origin blog.csdn.net/suyuaidan/article/details/132712085