A simple introduction to reactor

Reactor is a reactive programming framework based on reactive streams. Before understanding the project reactor project, you need to be familiar with the observer mode (subscription publishing mode) and reactive streams.
Only by understanding these contents can we better start learning project reactor. You can learn these two knowledge points simply by reading
my opinion on the observer mode and
an introduction to reactive streams backpressure responsive programming .

suggested study methods

The general steps and preconditions of learning reactor

  1. First understand the concept of synchronous and asynchronous, and understand why asynchrony is needed
  2. Understand the observer pattern and understand why the observer pattern is needed
  3. Understand reactive streams, at least know how the observer mode (subscription publishing mode) is required in reactive streams.
  4. To learn and use the operation method and style of stream in jdk 8
  5. Start learning reactor.

Suggestions when learning reactor:

  1. Understand the basic process of reactor before learning
  2. First understand the role and status of flux and mono in the subscription model, and don't be confused by the knowledge of the methods and operators of flux and mono to generate data.
  3. Understand the methods and calls of subscribe.
  4. You probably know the function and use of the scheduler, and you probably know that there is a hook and its function.
  5. Read about how flux and mono create data, and you can check it when you use it.
  6. Read about the operators of flux and mono, and you can check them when you use them.
  7. Get started with a real project, start using reactor, and consult the documentation when needed. If necessary in the project, learn context again.
  8. Wait for the reactor to get started with the project, and then try to optimize the project through the scheduler and hooks.

If you are for an interview, I didn't say it.

Data sources Flux and Mono

Reactor, like all publish-subscribe models, conforms to the reactive streams specification. So reactor also includes publisher, subscriber, subscription, processor, operator and other concepts.
Flux and Mono are the publishers implemented by reactor. They can accept subscriptions from other subscribers, generate data and push the data to subscribers. At the same time, they also integrate some operations on data streams, such as map, filter, etc.

the difference

Flux is a data stream containing 0 to N elements, and Mono is a data stream containing 0 or 1 elements.

basic process

Only by understanding the process of reactor in general can we not be lost by the concept of trivial matters. In fact, the entire reactor is a subscription-publishing model.
Flux and Mono are the default publishers of the entire system, the purpose is to simplify the work of publisher customization.

Flux and Mono integrate many operators to reduce the workload of our custom subscribers and processors.
Because of the existence of operators, we do not need to define our own processors and subscribers when we operate on data sources and elements. We can directly use the combination of operators to complete the work. Do not try to customize subscribers and processors unless you have to
.

创建数据源
调用操作符对数据进行处理
subscibe订阅数据源

Create a Flux/Mono data source

After understanding the publish-subscribe model and the role of publisher, you will understand flux and mono. In order to meet the needs of Flux and mono, there are a large number of methods for generating data.
Due to space constraints, I have organized this part of the content separately. For details, see Reactor’s data source generation

operator

In the basic process, it has been mentioned that reactor integrates many operators in order to reduce the workload of customizing subscribers and processors.
First of all, you should have a general understanding of the functions and application scenarios of operators, and you only need to know what types of operators there are.
You may wish to read the official documentation when you need it. Don’t remember the commonly used ones, because they are often used. It is not necessary to memorize the ones that are not commonly used, because they will not be used if they are memorized.
Because of space issues, I sorted out this part of the content separately. For details, see the operator of reactor

subscribe

The subscribe operator is used to subscribe to elements in the stream.
When the elements in the stream are not subscribed, all operations will not be triggered, and only when the elements in the stream are subscribed, all operations will be triggered.
The commonly used subscribe interface is as follows


Flux.subscribe();
/**
 * @param consumer 消费者接口,用来消费流中的元素
 *                 
 */
Flux.subscribe(Consumer<? super T> consumer);

/**
 * @param consumer 消费者接口,用来消费流中的元素
 * @param errorConsumer 错误消费者接口,用来消费流中的错误
 */
Flux.subscribe(Consumer<? super T> consumer, Consumer<? super Throwable> errorConsumer);

/**
 * @param consumer 消费者接口,用来消费流中的元素
 * @param errorConsumer 错误消费者接口,用来消费流中的错误
 * @param completeConsumer 完成消费者接口,用来消费流中的完成
 */
Flux.subscribe(Consumer<? super T> consumer, Consumer<? super Throwable> errorConsumer, Runnable completeConsumer);

/**
 * @param consumer 消费者接口,用来消费流中的元素
 * @param errorConsumer 错误消费者接口,用来消费流中的错误
 * @param completeConsumer 完成消费者接口,用来消费流中的完成
 * @param subscriptionConsumer 订阅消费者接口,用来消费流中的订阅
 */              
Flux.subscribe(Consumer<? super T> consumer, Consumer<? super Throwable> errorConsumer, Runnable completeConsumer, Consumer<? super Subscription> subscriptionConsumer)

Scheduler

Reactor can also be considered concurrency agnostic. That is, it doesn't enforce any concurrency model.
Going a step further, it puts the choice in the hands of developers. However, it does provide some convenient libraries for concurrent execution.
Reactor provides two ways to adjust the Scheduler in the reactive chain: publishOn and subscribeOn.
They both accept a Scheduler as a parameter, allowing the scheduler to be changed.
But the position of publishOn in the chain is particular, but subscribeOn does not matter.
publishOn It will change the execution thread of subsequent operators. And subscribeOn changes the scheduler of the downstream operator.

In Reactor, the execution mode and execution process depend on the Scheduler used.

  • Current thread (Schedulers.immediate())
  • Single thread (Schedulers.single())
  • Fixed size thread pool (Schedulers.parallel())
  • Elastic thread pool (Schedulers.elastic())
Flux.just(1, 2, 3)
        .publishOn(Schedulers.parallel()) //指定在parallel线程池中执行
        .map(i -> {
    
    
            System.out.println("map1: " + Thread.currentThread().getName());
            return i;
        })
        .publishOn(Schedulers.elastic()) // 指定下游的执行线程
        .map(i -> {
    
    
            System.out.println("map2: " + Thread.currentThread().getName());
            return i;
        })
        .subscribeOn(Schedulers.single())
        .subscribe(i -> System.out.println("subscribe: " + Thread.currentThread().getName()));

Additionally some operators use a specific scheduler.

Flux.interval(Duration.ofSeconds(1), Schedulers.single())
        .subscribe(System.out::println);

processor

Processor is an object that implements the Publisher and Subscriber interfaces, and it can be used to connect Publisher and Subscriber.
In most cases, you should avoid using Processors, they are difficult to use correctly, and are mainly used in some special scenarios.
Rather than using Reactor's Processors directly, a better way is to get Processor's Sink by calling sink() once.

FluxProcessor<String, String> processor = DirectProcessor.create();
processor.subscribe(System.out::println);
processor.onNext("foo");
processor.onNext("bar");
processor.onNext("baz");
processor.onComplete();

sink

Sink is an interface that defines some methods for sending data to Processor.

UnicastProcessor<Integer> processor = UnicastProcessor.create();
FluxSink<String> sink = processor.sink();
sink.next("foo");
sink.next("bar");
sink.next("baz");
sink.complete();

Overview of existing Processors

  • DirectProcessor: Direct processor, it is a synchronous processor, it will send all data to all subscribers.
  • UnicastProcessor: Unicast processor, it is a synchronous processor, it will only send data to the first subscriber.
  • ReplayProcessor: The replay processor, which is an asynchronous processor, sends all data to all subscribers, including those who subscribe after subscription.
  • WorkQueueProcessor: Work queue processor, which is an asynchronous processor, will send all data to all subscribers, including those who subscribe after subscription.
  • TopicProcessor: The topic processor, which is an asynchronous processor, sends all data to all subscribers, including those who subscribe after subscription.
  • EmitterProcessor: Emitter processor, which is an asynchronous processor, will send all data to all subscribers, including those who subscribe after subscription.

Hooks

Hooks can be regarded as a tool class. After setting, the methods set by Hooks will be called back for the following Flux and Mono, similar to the hooks of the operating system.

This part is regarded as a relatively advanced part of reactor. It is recommended to know such a concept before starting to use reactor for projects.
After doing one or two projects, go back and look at what hooks do

I split the content of this part, see: Hooks of reactor

Context

When switching from an imperative programming style to a reactive programming style, one of the biggest technical challenges is threading.
In imperative programming style, we can pass data through ThreadLocal,
but in reactive programming style, we cannot pass data through ThreadLocal.
Because threads are managed by Reactor, we have no control over thread creation and destruction.
Context is used to solve this problem. Context is an interface that defines methods for getting and setting data.

This part of the content is relatively difficult to understand. It is recommended to put learning and understanding later. In short, when you need to use the ThreadLocal class in a multi-threaded environment, it is not too late to learn this part.

String key = "key";
Mono<String> r = Mono.just("hello")
        .flatMap(s -> Mono.subscriberContext()
                .map(ctx -> s + " " + ctx.get(key)))
        .subscriberContext(ctx -> ctx.put(key, "world"));
r.subscribe(System.out::println);
// 输出:hello world

context api

Context is an interface similar to Map (this data structure): it stores key-value (key-value) pairs, and you need to get the value by key:

  • put method: Put a key-value pair into Context.
  • get method: get the value by key.
  • delete method: delete the key-value pair by key.
  • hasKey method: use key to determine whether there is a key-value pair.
  • stream method: returns a stream, which is used to traverse all key-value pairs in the Context.
  • isEmpty method: determine whether the Context is empty.
  • size method: returns the number of key-value pairs in the Context.
  • putAll method: Put all the key-value pairs in one Context into another Context.
  • currentContext method: returns the Context of the current thread.
  • empty method: returns an empty Context.
  • root method: returns an empty Context.

Bind context to Flux and writing

String key = "key";
Flux<String> r = Flux.just("hello")
        .flatMap(s -> Mono.subscriberContext()
        .subscriberContext(ctx -> ctx.put(key, "world"));
                .map(ctx -> s + " " + ctx.get(key)))

read data from context

String key = "key"
Flux<String> r = Flux.just("hello")
        .flatMap(s -> Mono.subscriberContext()
                .map(ctx -> s + " " + ctx.get(key)))
        .subscriberContext(ctx -> ctx.put(key, "world"));
r.subscribe(System.out::println);
// 输出:hello world

Guess you like

Origin blog.csdn.net/aofengdaxia/article/details/128975491