为什么需要响应性编程?

先来看看传统的Web应用有哪些缺点。

Thread per Request模型

比如使用Servlet开发的单体应用,部署到Tomcat,Tomcat有线程池,每个请求过来后都会交给线程池中的一个线程来执行,这就是Thread per Request模型,如果执行过程中包括访问数据库,或者包括读取文件,也就是IO操作,则在进行IO操作时,请求线程是阻塞的,即使是阻塞线程也是占用资源的,典型的每个线程要使用1MB的内存,而且这个线程不能处理其他请求。

如果有并发请求,则会同时有多个线程处于阻塞状态,每个线程占据一份资源。同时,Tomcat的线程池大小决定了可以同时处理多少个请求。这就意味着应用仅能处理并发数为线程池大小的请求。如果要处理更多的请求,就需要配置更大的线程池,但是线程占用内存(一般一个线程1MB的样子),线程数越多,占用的内存越大;同时线程越多又会带来线程上下文切换的消耗。

因此,当并发数很大的时候,Thread per Request模型很消耗资源。

等待I/O操作

在I/O操作中也存在大量的资源浪费:如调用数据库,读取文件等。

此时,发出I/O请求的线程会阻塞等待I/O操作的完成,即阻塞式I/O。这些线程的阻塞仅仅是为了等待一个响应,浪费了线程,浪费了内存。

【SpringWebFlux】响应式编程概述与入门_reactor

响应延迟

传统命令式编程另一个问题是:当一个服务需要做很多操作而不仅仅是I/O请求的时候,响应延迟相应的增大。

如服务A需要调用服务B和C,比如查询数据库,聚合结果并返回。意味着服务A的响应时间包括:

  • 服务B的响应时间(网络延迟时间+处理时间)
  • 服务C的响应时间(网络延迟时间+处理时间)
  • 数据库请求响应时间(网络延迟时间+处理时间)

【SpringWebFlux】响应式编程概述与入门_响应式编程_02

如果服务调用没有前后依赖关系,则可以并行调用服务。如果使用java的CompletableFuture异步调用并注册回调,开发会复杂很多,而且阅读和维护也会复杂很多。

压垮客户端

微服务的另一个问题是:服务A请求服务B的数据,如果数据量很大,超过了服务A能处理的程度,则导致服务OOM。

总结

上述的问题就是响应式编程要解决的。
响应式编程的优势:

  • 不用Thread per Request模型,使用少量线程即可处理大量的请求。
  • 在执行I/O操作时不让线程等待。
  • 简化并行调用。
  • 支持背压,让客户端告诉服务端它可以处理多少负载。

响应式流规范

响应式流规范的说明

响应式流规范网址:http://www.reactive-streams.org

响应式流的规范文档:https://github.com/reactive-streams/reactive-streams-jvm/blob/v1.0.3/README.md

响应式规范发布了一组接口,用于实现。

maven仓库地址:

<dependency>
    <groupId>org.reactivestreams</groupId>
    <artifactId>reactive-streams</artifactId>
    <version>1.0.3</version>
</dependency>
<dependency>
    <groupId>org.reactivestreams</groupId>
    <artifactId>reactive-streams-tck</artifactId>
    <version>1.0.3</version>
</dependency>
<dependency>
    <groupId>org.reactivestreams</groupId>
    <artifactId>reactive-streams-tck-flow</artifactId>
    <version>1.0.3</version>
</dependency>
<dependency>
    <groupId>org.reactivestreams</groupId>
    <artifactId>reactive-streams-examples</artifactId>
    <version>1.0.3</version>
</dependency>

响应式流(Reactive Streams)规范,规定了异步组件之间使用背压进行交互。

响应式流在Java 9中使用Flow API适配。Flow API是互操作的规范,而不是具体的实现,它的语义跟响应式流规范一致。

【SpringWebFlux】响应式编程概述与入门_reactor_03

响应式流规范的接口规范

Publisher表示数据流的生产者或数据源,包含一个方法让订阅者注册到发布者,Publisher代表了发布者和订阅者直接连接的标准化入口点。

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

Subscriber:表示消费者,onSubscribe方法为我们提供了一种标准化的方式来通知Subscriber订阅成功。

public interface Subscriber<T> {
	public void onSubscribe(Subscription s);
	public void onNext(T t);
	public void onError(Throwable t);
	public void onComplete();
}
  • onSubscribe:发布者在开始处理之前调用,并向订阅者传递一个订阅票据对象(Subscription)。
  • onNext:用于通知订阅者发布者发布了新的数据项。
  • onError:用于通知订阅者,发布者遇到了异常,不再发布数据事件。
  • onComplete:用于通知订阅者所有的数据事件都已发布完。

Subscription:onSubscribe方法的传入参数引入一个名为Subscription(订阅)的订阅票据。Subscription为控制元素的生产提供了基础。

public interface Subscription {
	public void request(long n);
	public void cancel();
}
  • request:用于让订阅者通知发布者随后需要发布的元素数量。
  • cancel:用于让订阅者取消发布者随后的事件流。

Processor:如果实体需要转换进来的项目,并将转换后的项目传递给另一个订阅者,此时需要Processor接口。

该接口既是订阅者,又是发布者:

public interface Processor<T, R> extends Subscriber<T>, Publisher<R> {
}

所有接口都存在于org.reactivestreams包中:

【SpringWebFlux】响应式编程概述与入门_响应式编程_04

JDK9中同样有这些接口。

【SpringWebFlux】响应式编程概述与入门_mono_05

底层机制如下图:

【SpringWebFlux】响应式编程概述与入门_mono_06

响应式规范实战

这里使用的是JDK9中的API。

Publisher+Subscriber的使用

package com.morris.spring.webflux.reactivestream;

import java.util.concurrent.Flow;
import java.util.concurrent.SubmissionPublisher;
import java.util.concurrent.TimeUnit;
import java.util.stream.IntStream;

/**
 * Publisher+Subscriber的使用
 */
public class PublisherSubscriberDemo {
    public static void main(String[] args) throws InterruptedException {

        // 创建订阅者
        Flow.Subscriber<Integer> subscriber = new Flow.Subscriber<>() {

            private Flow.Subscription subscription;

            @Override
            public void onSubscribe(Flow.Subscription subscription) {
                this.subscription = subscription;
                subscription.request(1);
            }

            @Override
            public void onNext(Integer item) {
                System.out.println("item: " + item);
                subscription.request(1);
            }

            @Override
            public void onError(Throwable throwable) {
                throwable.printStackTrace();
            }

            @Override
            public void onComplete() {
                System.out.println("onComplete");
            }
        };

        // 创建发布者
        SubmissionPublisher<Integer> publisher = new SubmissionPublisher<>();

        // 发布者与订阅者建立关系
        publisher.subscribe(subscriber);

        // 发布消息
        IntStream.range(0, 100).boxed().forEach(t -> publisher.submit(t));

        // 休眠1s,防止主程序退出时,publisher中的线程还没启动,导致发布订阅的逻辑不会执行
        TimeUnit.SECONDS.sleep(1);
    }
}

Processor的使用

package com.morris.spring.webflux.reactivestream;

import java.util.concurrent.Flow;
import java.util.concurrent.SubmissionPublisher;
import java.util.concurrent.TimeUnit;
import java.util.stream.IntStream;

/**
 * Processor的使用
 * Processor既是Publisher,又是Subscriber
 */
public class ProcessorDemo {

    public static void main(String[] args) throws InterruptedException {

        // 发布者
        SubmissionPublisher<Integer> publisher = new SubmissionPublisher();

        // processor
        Flow.Processor<Integer, String> processor = new Flow.Processor<>() {

            private Flow.Subscription subscription;

            private Flow.Subscriber<? super String> subscriber;

            @Override
            public void onSubscribe(Flow.Subscription subscription) {
                this.subscription = subscription;
                subscriber.onSubscribe(subscription);
            }

            @Override
            public void onNext(Integer item) {
                subscription.request(1);
                subscriber.onNext("item" + item);
            }

            @Override
            public void onError(Throwable throwable) {
                subscriber.onError(throwable);
            }

            @Override
            public void onComplete() {
                subscriber.onComplete();
            }

            @Override
            public void subscribe(Flow.Subscriber<? super String> subscriber) {
                this.subscriber = subscriber;
            }
        };

        // 创建订阅者
        Flow.Subscriber<String> subscriber = new Flow.Subscriber<>() {

            private Flow.Subscription subscription;

            @Override
            public void onSubscribe(Flow.Subscription subscription) {
                this.subscription = subscription;
                subscription.request(1);
            }

            @Override
            public void onNext(String item) {
                System.out.println(item);
                subscription.request(1);
            }

            @Override
            public void onError(Throwable throwable) {
                throwable.printStackTrace();
            }

            @Override
            public void onComplete() {
                System.out.println("onComplete");
            }
        };

        // 建立publisher与processor的关系
        publisher.subscribe(processor);

        // 建立processor与subscriber的关系
        processor.subscribe(subscriber);

        IntStream.rangeClosed(1, 100).boxed().forEach(t -> publisher.submit(t));

        // 休眠1s,防止主程序退出时,publisher中的线程还没启动,导致发布订阅的逻辑不会执行
        TimeUnit.SECONDS.sleep(1);
    }

}