WebFlux that laymen can understand, missed the blood loss

Preface

Only a bald head can become stronger.
The text has been included in my GitHub repository, welcome to Star:
https://github.com/ZhongFuCheng3y/3y

Knowledge point structure of this article:

WebFlux that laymen can understand, missed the blood loss
Knowledge point architecture
If there are students who follow my public account articles, they will find that recently I have reposted some better WebFlux articles from time to time because I am studying recently.

I also said before that before learning a technology, we must first understand why we should learn this technology. In fact, I didn’t have much original motivation to learn WebFlux this time. The main reason is that we will take turns to share the technology in our group, and I don’t know what to share...

I was learning big data-related knowledge before, but the timeline of this piece will be longer, and I feel that I can’t keep up with sharing in the group (and most of the students in the group understand big data, and I am the only one with tears) . So, what I thought was: "How about I learn something new and engage in it?". So I spent some time learning WebFlux~

This article mainly explains what WebFlux is, and leads you through the door, hoping to help you (at least after reading this article, I know what WebFlux is for)

1. What is WebFlux?

We can see the introduction of WebFlux by pulling a little bit from Spring's official website

WebFlux that laymen can understand, missed the blood loss
Introduction of WebFlux
What kind of information can we draw from the introduction of the official website?

  • Our programmers often choose different technologies according to different application scenarios. Some scenarios are suitable for synchronous blocking, and some are suitable for asynchronous non-blocking. And Spring5 provides a complete set of reactive (non-blocking) technology stacks for us to use (including Web controllers, permission control, data access layer, etc.).
  • The picture on the left is a comparison of technology stacks;
  • Responsive type generally uses Netty or Servlet 3.1 container (because asynchronous non-blocking is supported), while Servlet technology stack uses Servlet container
    on the Web side, reactive type uses WebFlux, and Servlet uses SpringMVC
  • …..
    To sum up, WebFlux is only a part of reactive programming (on the Web control side), so we generally use it to compare with SpringMVC.

2. How to understand reactive programming?

Reactive Programming was mentioned above, and WebFlux is just one of the technology stacks of reactive programming, so let’s first explore what is reactive programming

The definition we got from Wikipedia:

reactive programming is a declarative programming paradigm concerned with data streams and the propagation of change

Reactive programming is a declarative programming paradigm based on data stream and propagation of change

There is also a small example on Wikipedia:

WebFlux that laymen can understand, missed the blood loss
The
meaning of the example is roughly as follows:

  • In imperative programming (our daily programming mode), the formula a=b+c, ​​which means that the value of a is calculated by b and c. If there is a subsequent change in b or c, it will not affect the value of a
  • In reactive programming, the formula a:=b+c, ​​which means that the value of a is calculated by b and c. But if the value of b or c changes subsequently, it will affect the value of a.
    I think the above example can help us understand the propagation of change (propagation of change)

How do you understand data stream and declarative? Then we can mention our Stream. I have written articles about Lambda expressions and Stream streams before. You can check it out first:

  • Lambda expression basics recently learned
  • Let’s take you to experience the
    Lambda syntax of Stream streaming like this (the use of Stream streaming involves a lot of Lambda expressions, so generally learn Lambda first and then Stream streaming):

WebFlux that laymen can understand, missed the blood loss

The use of grammar Stream is divided into three steps (creating Stream, performing intermediate operations, and performing final operations):

WebFlux that laymen can understand, missed the blood loss
The three-step
implementation of intermediate operations actually provides us with a lot of APIs to manipulate the data in the Stream stream (summing/de-duplication/filtering), etc.

WebFlux that laymen can understand, missed the blood loss
The intermediate operation explained
so much, how to understand the data flow and declarative? It's actually like this:

  • Originally the data was processed by ourselves. Later, we abstracted the data to be processed (turned into a data stream), and then processed the data in the data stream (declaratively) through the API. For
    example, the following code; The data becomes a data stream, and the data in the data stream is processed by explicitly calling .sum() to get the final result:
public static void main(String[] args) {
    int[] nums = { 1, 2, 3 };
    int sum2 = IntStream.of(nums).parallel().sum();
    System.out.println("结果为:" + sum2);
}

As shown below:

WebFlux that laymen can understand, missed the blood loss
Data flow and declarative

2.1 Reactive programming -> asynchronous non-blocking

The above mentioned what reactive programming is:

Reactive programming is a declarative programming paradigm based on data stream and propagation of change

It also explained what data flow/change transfer/declarative means, but when it comes to reactive programming, asynchronous non-blocking is inseparable.

From the introduction of WebFlux information on the Spring official website, we can find the words asynchronous, nonblocking, because reactive programming is asynchronous, and it can also be understood that change delivery is executed asynchronously.

As shown in the figure below, the total amount will be affected by other amounts (the update process is asynchronous):

WebFlux that laymen can understand, missed the blood loss
The total amount of money will be affected by other amounts.
Our JDK8 Stream stream is synchronous, which is not suitable for reactive programming (but the basic usage needs to be understood, because reactive stream programming is an operation stream)

Reactive streaming is already supported in JDK9, let’s take a look

三、JDK9 Reactive

The specification of responsive streaming has already been proposed: it mentions:

Reactive Streams is an initiative to provide a standard for asynchronous stream processing with non-blocking back pressure ----->http://www.reactive-streams.org/

Translate and add some information:

Reactive Streams provides a standard for achieving asynchronous non-blocking backpressure by defining a set of entities, interfaces and interoperability methods. Third parties follow this standard to implement specific solutions, such as Reactor, RxJava, Akka Streams, and Ratpack.

The specification actually defines four interfaces:

WebFlux that laymen can understand, missed the blood loss
The standard four interfaces
Java platform did not provide complete support for Reactive until JDK 9. JDK9 also defines the four interfaces mentioned above, in the java.util.concurrent package

WebFlux that laymen can understand, missed the blood loss

A general stream processing architecture of Java's reactive stream interface will generally look like this (producers generate data, perform intermediate processing on the data, and consumers get the data for consumption):

WebFlux that laymen can understand, missed the blood loss
Streaming architecture

  • The data source, generally called the producer (Producer)
  • The destination of the data, generally called the consumer (Consumer)
  • During processing, one or more processing stages are performed on the data. (Processor)
    Here we look back at the interface of the reactive stream, we should be able to understand:

  • Publisher (publisher) is equivalent to producer (Producer)
  • Subscriber (subscriber) is equivalent to consumer (Consumer)
  • Processor is used to process data between publishers and subscribers. The
    concept of back pressure is mentioned in the reactive stream, which is actually very easy to understand. The realization of asynchronous non-blocking in the reactive stream is based on the producer and consumer model, and a problem that is prone to occur for producer consumers is that if the producer produces too much data, the consumer is overwhelmed.

The back pressure is plainly: consumers can tell producers how much data they need. Here is what the Subscription interface does.

Let's take a look at the methods of the JDK9 interface, and perhaps better understand what is said above:

// 发布者(生产者)
public interface Publisher<T> {
    public void subscribe(Subscriber<? super T> s);
}
// 订阅者(消费者)
public interface Subscriber<T> {
    public void onSubscribe(Subscription s);
    public void onNext(T t);
    public void onError(Throwable t);
    public void onComplete();
}
// 用于发布者与订阅者之间的通信(实现背压:订阅者能够告诉生产者需要多少数据)
public interface Subscription {
    public void request(long n);
    public void cancel();
}
// 用于处理发布者 发布消息后,对消息进行处理,再交由消费者消费
public interface Processor<T,R> extends Subscriber<T>, Publisher<R> {
}

3.1 Look at an example

There are a lot of comments in the code, so I won’t have too many BBs. I suggest you copy and run it directly:

class MyProcessor extends SubmissionPublisher<String>
        implements Processor<Integer, String> {

    private Subscription subscription;

    @Override
    public void onSubscribe(Subscription subscription) {
        // 保存订阅关系, 需要用它来给发布者响应
        this.subscription = subscription;

        // 请求一个数据
        this.subscription.request(1);
    }

    @Override
    public void onNext(Integer item) {
        // 接受到一个数据, 处理
        System.out.println("处理器接受到数据: " + item);

        // 过滤掉小于0的, 然后发布出去
        if (item > 0) {
            this.submit("转换后的数据:" + item);
        }

        // 处理完调用request再请求一个数据
        this.subscription.request(1);

        // 或者 已经达到了目标, 调用cancel告诉发布者不再接受数据了
        // this.subscription.cancel();
    }

    @Override
    public void onError(Throwable throwable) {
        // 出现了异常(例如处理数据的时候产生了异常)
        throwable.printStackTrace();

        // 我们可以告诉发布者, 后面不接受数据了
        this.subscription.cancel();
    }

    @Override
    public void onComplete() {
        // 全部数据处理完了(发布者关闭了)
        System.out.println("处理器处理完了!");
        // 关闭发布者
        this.close();
    }

}

public class FlowDemo2 {

    public static void main(String[] args) throws Exception {
        // 1. 定义发布者, 发布的数据类型是 Integer
        // 直接使用jdk自带的SubmissionPublisher
        SubmissionPublisher<Integer> publiser = new SubmissionPublisher<Integer>();

        // 2. 定义处理器, 对数据进行过滤, 并转换为String类型
        MyProcessor processor = new MyProcessor();

        // 3. 发布者 和 处理器 建立订阅关系
        publiser.subscribe(processor);

        // 4. 定义最终订阅者, 消费 String 类型数据
        Subscriber<String> subscriber = new Subscriber<String>() {

            private Subscription subscription;

            @Override
            public void onSubscribe(Subscription subscription) {
                // 保存订阅关系, 需要用它来给发布者响应
                this.subscription = subscription;

                // 请求一个数据
                this.subscription.request(1);
            }

            @Override
            public void onNext(String item) {
                // 接受到一个数据, 处理
                System.out.println("接受到数据: " + item);

                // 处理完调用request再请求一个数据
                this.subscription.request(1);

                // 或者 已经达到了目标, 调用cancel告诉发布者不再接受数据了
                // this.subscription.cancel();
            }

            @Override
            public void onError(Throwable throwable) {
                // 出现了异常(例如处理数据的时候产生了异常)
                throwable.printStackTrace();

                // 我们可以告诉发布者, 后面不接受数据了
                this.subscription.cancel();
            }

            @Override
            public void onComplete() {
                // 全部数据处理完了(发布者关闭了)
                System.out.println("处理完了!");
            }

        };

        // 5. 处理器 和 最终订阅者 建立订阅关系
        processor.subscribe(subscriber);

        // 6. 生产数据, 并发布
        publiser.submit(-111);
        publiser.submit(111);

        // 7. 结束后 关闭发布者
        // 正式环境 应该放 finally 或者使用 try-resouce 确保关闭
        publiser.close();

        // 主线程延迟停止, 否则数据没有消费就退出
        Thread.currentThread().join(1000);
    }

}

The output result is as follows:

WebFlux that laymen can understand, missed the blood loss
The output
process is actually very simple:

WebFlux that laymen can understand, missed the blood loss
Process
reference materials:

Java 8's Stream mainly focuses on the filtering, mapping, and merging of streams, while Reactive Stream goes one step further, focusing on the generation and consumption of streams, that is, the coordination of streams between production and consumers

To put it bluntly: Responsive streaming is asynchronous and non-blocking + flow control (you can tell the producer how much you need/unsubscribe relationship)

Looking forward to the scenario application of reactive programming:

For example, in a log monitoring system, our front-end page will no longer need to continuously request data from the server and then update it through "command-style" polling. Instead, after the channel is established, the data flow will continue to flow from the system to the page. So as to show the real-time indicator change curve;
another example is a social platform, friends' dynamics, likes and comments are not manually refreshed, but are automatically reflected on the interface when the background data changes.

Fourth, getting started with WebFlux

After pulling a lot, I finally returned to WebFlux. After the above foundation, we are now able to draw some conclusions:

  • WebFlux is part of Spring's launch of reactive programming (web side)
  • Reactive programming is asynchronous and non-blocking (a declarative programming paradigm based on data stream and propagation of change).
    Let’s go back and look at the official website:

WebFlux that laymen can understand, missed the blood loss
mvc or webflux

4.1 Simple experience of WebFlux

Spring official in order to make us faster/smooth to WebFlux, the previous SpringMVC set was supported. In other words: we can use WebFlux like SpringMVC.

WebFlux that laymen can understand, missed the blood loss

The reactive stream used by WebFlux that supports SpringMVC does not use the JDK9 platform, but a reactive stream library called Reactor. So, getting started with WebFlux is actually more about understanding how to use Reactor's API, let’s take a look~

Reactor is a reactive stream, and it also has a corresponding publisher (Publisher). The publisher of Reactor is represented by two classes:

  • Mono (return 0 or 1 element)
  • Flux (returns 0-n elements)
    and the consumer is the Spring framework to help us complete

Let's look at a simple example (built based on the WebFlux environment):

// 阻塞5秒钟
private String createStr() {
    try {
        TimeUnit.SECONDS.sleep(5);
    } catch (InterruptedException e) {
    }
    return "some string";
}

// 普通的SpringMVC方法
@GetMapping("/1")
private String get1() {
    log.info("get1 start");
    String result = createStr();
    log.info("get1 end.");
    return result;
}

// WebFlux(返回的是Mono)
@GetMapping("/2")
private Mono<String> get2() {
    log.info("get2 start");
    Mono<String> result = Mono.fromSupplier(() -> createStr());
    log.info("get2 end.");
    return result;
}

First of all, it is worth noting that when we build the WebFlux environment, the application server defaults to Netty:

WebFlux that laymen can understand, missed the blood loss
Based on Netty,
let's visit the SpringMVC interface and the WebFlux interface separately to see what is the difference:

SpringMVC:

WebFlux that laymen can understand, missed the blood loss
SpringMVC
WebFlux:

WebFlux that laymen can understand, missed the blood loss

From the point of view of the caller (browser), WebFlux does not perceive any changes, because it has to wait 5 seconds before returning data. However, we can see from the server log that WebFlux returns the Mono object directly (instead of synchronously blocking for 5s like SpringMVC, the thread returns).

This is the advantage of WebFlux: it can handle high concurrency with a fixed thread (to give full play to the performance of the machine).

WebFlux also supports server push (SSE -> Server Send Event), let’s look at an example:

/**
     * Flux : 返回0-n个元素
     * 注:需要指定MediaType
     * @return
     */
@GetMapping(value = "/3", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
private Flux<String> flux() {
    Flux<String> result = Flux
        .fromStream(IntStream.range(1, 5).mapToObj(i -> {
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
            }
            return "flux data--" + i;
        }));
    return result;
}

The effect is to push data to the browser every second:

WebFlux that laymen can understand, missed the blood loss

I haven't finished writing WebFlux on server push . This article writes about WebFlux supporting SpringMVC annotations for development. The next article writes about how to use WebFlux another mode (Functional Endpoints) to develop and some common problems need to be added~

Two years of painstaking articles: "Interview Questions", "Basics" and "Advanced" are all here!

WebFlux that laymen can understand, missed the blood loss
More than 300 original articles technical articles
massive video resources
exquisite mind map
face questions
press scan code may be concerned about obtaining
watching and sharing is very important to me!
It is not easy to create. Your support and recognition is the greatest motivation for my creation. See you in the next article! Seek likes and attention️ Seek to share and leave a message

Guess you like

Origin blog.51cto.com/15082392/2590299