webflux学习分享

5 篇文章 0 订阅

目录

基础知识

lambda

stream

响应式编程

1. 事件驱动

2. 实时响应

3. 弹性机制

响应式流规范(Reactive Streams)

reactive stream(响应式流)

定义

异步非阻塞

流量控制

基于响应式流的“升级版”的响应式编程

Reactor

Flux与Mono

核心调用过程

Servlet3.0

WebFlux

服务端技术栈

响应式Http客户端

参考文档


基础知识

在了解学习webflux之前,需要学习和掌握一些基本知识,主要包括响应式编程和lambda相关。

lambda

取函数式编程之所长。

students.sort(new Comparator<Student>() {

@Override

public int compare(Student s1, Student s2) {

return Double.compare(s1.getHeight(), s2.getHeight());

}

});

//lambda

students.sort((s1, s2) -> Double.compare(s1.getHeight(), s2.getHeight()));

为什么?奥妙在哪?

java8开始存在,定义了FunctionalInterface接口。这种只有一个接口函数需要被实现的接口类型,我们叫它”函数式接口“。

以lambda作为参数的方法能够推断出来lambda所表示的是哪个函数式接口的那个抽象方法。

在Java 8里面,所有的Lambda的类型都是一个接口,而Lambda表达式本身,也就是”那段代码“,需要是这个接口的实现。这是我认为理解Lambda的一个关键所在,简而言之就是,Lambda表达式本身就是一个接口的实现。

stream

Stream(流)是一个来自数据源的元素队列并支持聚合操作

分为 中间操作 和 最终操作,在最终操作没有调用的情况下,所有的中间操作都不会执行。

 

Stream流有一些特性:

  1. Stream流不是一种数据结构,不保存数据,它只是在原数据集上定义了一组操作。
  2. 这些操作是惰性的,即每当访问到流中的一个元素,才会在此元素上执行这一系列操作。
  3. Stream不保存数据,故每个Stream流只能使用一次。

 

响应式编程

1. 事件驱动

在一个 事件驱动 的应用程序中,组件之间的交互是通过松耦合的 生产者 (production)和 消费者 (consumption) 来实现的。这些事件是以 异步 和 非阻塞 的方式发送和接收的。

事件驱动 的系统依靠 推模式 而不是 拉模式 或 投票表决,即 生产者 是在有消息时才推送数据给 消费者,而不是通过一种浪费资源方式:让 消费者 不断地 轮询 或 等待数据。

2. 实时响应

程序发起执行以后,应该 快速 返回存储 结果的上下文,把具体执行交给 后台线程。待处理完成以后,异步地将 真实返回值 封装在此 上下文 中,而不是 阻塞 程序的执行。实时响应是通过 异步 编程实现的,例如:发起调用后,快速返回类似 java8 中 CompletableFuture 对象。

3. 弹性机制

事件驱动的 松散耦合 提供了组件在失败下,可以抓获 完全隔离 的上下文场景,作为 消息封装,发送到下游组件。在具体编程时可以 检查错误 ,比如:是否接收到,接收的命令是否可执行等,并决定如何应对。

 

响应式流规范(Reactive Streams)

在 Java 平台上,Netflix(开发了 RxJava)、TypeSafe(开发了 ScalaAkka)、Pivatol(开发了 SpringReactor)共同制定了一个被称为 Reactive Streams 项目(规范),用于制定反应式编程相关的规范以及接口。java社区的响应式流规范(响应式流规范

Reactive Streams 由以下几个组件组成:

  • 发布者:发布元素到订阅者
  • 订阅者:消费元素
  • 订阅:在发布者中,订阅被创建时,将与订阅者共享
  • 处理器:发布者与订阅者之间处理数据

其主要的接口有这三个:

  • Publisher
  • Subscriber
  • Subcription

 

1.Publisher是能够发出元素的发布者。

public interface Publisher<T> {

    public void subscribe(Subscriber<? super T> s);

}

这里强调一下 Publisher 接口中的 subscribe 方法语义上有些奇特,它表示的不是订阅关系,而是被订阅关系。即 aPublisher.subscribe(aSubscriber) 表示的是 aPublisher 被 aSubscriber 订阅。

2.Subscriber是接收元素并做出响应的订阅者。

public interface Subscriber<T> {

    public void onSubscribe(Subscription s);

    public void onNext(T t);

    public void onError(Throwable t);

    public void onComplete();

}

当执行subscribe方法时,发布者会回调订阅者的onSubscribe方法,这个方法中,通常订阅者会借助传入的Subscription向发布者请求n个数据。然后发布者通过不断调用订阅者的onNext方法向订阅者发出最多n个数据。如果数据全部发完,则会调用onComplete告知订阅者流已经发完;如果有错误发生,则通过onError发出错误数据,同样也会终止流。

订阅后的回调用表达式表示就是onSubscribe onNext* (onError | onComplete)?,即以一个onSubscribe开始,中间有0个或多个onNext,最后有0个或1个onError或onComplete事件。

Publisher和Subscriber融合了迭代器模式和观察者模式。

我们经常用到的Iterable和Iterator就是迭代器模式的体现,可以满足上边第1和2个特点关于按需处理数据流的要求;而观察者模式基于事件的回调机制有助于满足第3个特点关于异步传递元素的要求。
 

3.SubscriptionPublisherSubscriber的“中间人”。

public interface Subscription {

    public void request(long n);

    public void cancel();

}

 

 

4.ProcessorPublisherSubscriber于一身。

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

}

 

reactive stream(响应式流)

定义

具备“异步非阻塞”特性和“流量控制”能力的数据流,我们称之为响应式流(Reactive Stream)。

 

异步非阻塞

cpu很快,io很慢,所以需要非阻塞

异步—多线程(但是多线程也有线程切换和线程资源的问题)

非阻塞—回调和CompletableFuture(代码太难写)

 

 

流量控制

 

发布者发出数据的速度和订阅者处理数据的速度不同的时候,怎么办呢?

向上游反馈流量请求的机制就叫做回压(backpressure)

 

回压有不同策略:缓存策略和丢弃策略

 

基于响应式流的“升级版”的响应式编程

Reactor3和RxJava2都是具有以上特点的响应式流的具体实现库。

 

 

Reactor

 

这是一个基于 Java 8 的实现了响应式流规范 (Reactive Streams specification)的响应式库。

Flux与Mono

Reactor中的发布者(Publisher)由FluxMono两个类定义,它们都提供了丰富的操作符(operator)。一个Flux对象代表一个包含0..N个元素的响应式序列,而一个Mono对象代表一个包含零/一个(0..1)元素的结果。

核心调用过程

Reactor 的核心调用过程大致可以分为图中的几个阶段

  • 声明:无论是使用 just 或者其他什么方式创建反应式流,这个过程都可以称之为声明,因为此时这些代码不会被实际的执行。
  • subscribe:当调用 subscribe 时,整个执行过程便进入 subscribe 阶段,经过一系列的调用之后,subscribe 动作会代理给具体的 Flux 来实现。
  • onSubscribe:onSubscribe 阶段指的是 Subscriber#onSubscribe 方法被依次调用的阶段。这个阶段会让各 Subscriber 知道 subscribe 方法已被触发,真正的处理流程马上就要开始。
  • request:onSubscribe 阶段是表示订阅动作的方式,让各 Subscriber 知悉,准备开始处理数据。当最终的 Subscriber 做好处理数据的准备之后,它便会调用 - Subscription 的 request 方法请求数据。
  • onNext:通过调用 Subscriber 的 onNext 方法,进行真正的响应式的数据处理。
  • onComplete:成功的终端状态,没有进一步的事件将被发送。
  • onError:错误的终端状态(和 onComplete 一样,当发生时,后面的将不会在继续执行)。

反应式编程和传统风格的编程一个最大的不同点在于,开发人员编写的大部分代码都是在声明处理过程。即这些代码并不会被实际的执行,直到开始被订阅。这便是为何第一阶段是声明阶段的原因。

 

如何实现?看下demo吧

Servlet3.0

 Servlet 3.0 新增了请求的异步处理,使用异步servlet主要原因就是因为,在service方法中业务逻辑如果碰到io操作时间比较长的操作,这样这个service方法就会长时间占用tomcat容器线程池中的线程,这样是不利于其他请求的处理的,当线程池中的线程处理任务时,任务由于长时间io操作,肯定会阻塞线程处理其他任务,引入异步servlet的目的就是将容器线程池和业务线程池分离开。在处理大io的业务操作的时候,把这个操作移动到业务线程池中进行,释放容器线程,使得容器线程处理其他任务,在业务逻辑执行完毕之后,然后在通知tomcat容器线程池来继续后面的操作,这个操作应该是把处理结果commit到客户端或者是dispatch到其他servlet上。

springmvc使用方式:

@Controller

public class PageController {

 

    @RequestMapping("/async/test")

    @ResponseBody

    public Callable<String> callable() {

        // 这么做的好处避免web server的连接池被长期占用而引起性能问题,

        // 调用后生成一个非web的服务线程来处理,增加web服务器的吞吐量。

        return new Callable<String>() {

            @Override

            public String call() throws Exception {

                Thread.sleep(3 * 1000L);

                return "小单 - " + System.currentTimeMillis();

            }

        };

    }

 

}

 

WebFlux

Spring WebFlux是随Spring 5推出的响应式Web框架。

服务端技术栈

Spring提供了完整的支持响应式的服务端技术栈。

如上图所示,左侧为基于spring-webmvc的技术栈,右侧为基于spring-webflux的技术栈,

  • Spring WebFlux是基于响应式流的,因此可以用来建立异步的、非阻塞的、事件驱动的服务。它采用Reactor作为首选的响应式流的实现库,不过也提供了对RxJava的支持。
  • 由于响应式编程的特性,Spring WebFlux和Reactor底层需要支持异步的运行环境,比如Netty和Undertow;也可以运行在支持异步I/O的Servlet 3.1的容器之上,比如Tomcat(8.0.23及以上)和Jetty(9.0.4及以上)。
  • 从图的纵向上看,spring-webflux上层支持两种开发模式:
    • 类似于Spring WebMVC的基于注解(@Controller、@RequestMapping)的开发模式;·
    • Java 8 lambda 风格的函数式开发模式。
  • Spring WebFlux也支持响应式的Websocket服务端开发。

例子:

@RestController

    public class HelloController {

 

        @GetMapping("/hello")

        public Mono<String> hello() {   // 【改】返回类型为Mono<String>

            return Mono.just("Welcome to reactive world ~");     // 【改】使用Mono.just生成响应式数据

        }

    }

 

响应式Http客户端

 

此外,Spring WebFlux也提供了一个响应式的Http客户端API WebClient。它可以用函数式的方式异步非阻塞地发起Http请求并处理响应。其底层也是由Netty提供的异步支持。

我们可以把WebClient看做是响应式的RestTemplate,与后者相比,前者:

  • 是非阻塞的,可以基于少量的线程处理更高的并发;
  • 可以使用Java 8 lambda表达式;
  • 支持异步的同时也可以支持同步的使用方式;
  • 可以通过数据流的方式与服务端进行双向通信。

当然,与服务端对应的,Spring WebFlux也提供了响应式的Websocket客户端API。

例子:

@Test

    public void webClientTest1() throws InterruptedException {

        WebClient webClient = WebClient.create("http://localhost:8080");   // 1

        Mono<String> resp = webClient

                .get().uri("/hello"// 2

                .retrieve() // 3

                .bodyToMono(String.class);  // 4

        resp.subscribe(System.out::println);    // 5

        TimeUnit.SECONDS.sleep(1);  // 6

    }

 

 

参考文档

 

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值