ReactiveWeb开发实战——Reactive编程!

Reactive Web开发实战

Spring WebFlux是Web领域的Reactive Programming(响应式编程)框架。Reactive Programming在近几年发展非常迅速,许多大公司也开始采用响应式编程。即使这样,Reactive Programming的概念对很多编程人员来说还是比较陌生,市面上讲解响应式编程的书籍也比较少。本章基于笔者在项目开发过程中积累的一些经验,通过一个实例介绍响应式编程的相关知识,让读者更好地理解Spring WebFlux框架。

Reactive编程

本节首先介绍响应式编程范式,以及基于该范式的ReactiveStreams标准API,最后给出基于Reactive Streams实现的JDK 9的Flow代码示例。

响应式宣言

说到响应式编程,就不能不提The Reactive Manifesto(响应式宣言)。几年前,一个大型应用系统可能会部署在几百台服务器上,响应时间为秒级,每天产生GB级的数据。随着移动设备的普及,应用程序需要部署在数以千计或万计的云端集群上,用户对响应时间的需求也提高到了毫秒级,每天产生的数据也达到了PB级,这对当今的系统架构提出了新的挑战。基于此,一些组织开发出了响应式系统。响应式系统具有4个特性,如图12.1所示。

可响应:系统尽可能地响应。

可恢复:系统出错的情况下也可以响应。

可伸缩:系统在各种负载下都可以响应。

消息驱动:系统通过异步传递消息。

以上4个特性就组成了响应式宣言,为响应式编程指明了方向。响应式系统就是以事件驱动,打造可伸缩、可恢复、能实时响应的应用程序。

Reactive编程简介

关于响应式编程,百度百科中是这样解释的:

在计算机领域,响应式编程是一个专注于数据流和变化传递的异步编程范式。这意味着可以使用编程语言很容易地表示静态(如数组)或动态(如事件发射器)数据流,在执行过程中数据流之间有一定的关系,关系的存在有利于数据流的自动变更。

上面的解释是不是不太好理解?我们具体分析一下。首先,响应式编程是一个编程范式,是一种编程规范,和我们平时开发中的声明式编程、命令式编程、函数式编程一样。其次,从过去的面向过程开发到Java提出的面向对象开发,响应式编程代表未来的发展方向——面向流开发。因此我们总结出响应式编程的定义是:一种面向数据流的响应式编码方式。

注意:Reactive Programming = Streams + Operations。其中,Streams代表被处理的数据节点,Operations代表那些异步处理函数。

 Reactive Streams标准

既然有了编程规范,就需要定义一套API协议标准。2013年,Netflix、Pivotal和Lightbend的工程师们启动了Reactive Streams项目。Reactive Stream(响应式流)是一套标准,是一套基于发布/订阅模式的数据流处理规范。对于开发人员来说,它其实就是一个API规范,具有异步非阻塞背压特性,异步非阻塞可以在同等资源下给出更快的响应。

举个直观的例子可以帮助读者更好地理解响应式数据流。现代前端开发框架如Vue.js和React等实现了双向数据绑定,在一个输入框内修改数据,可以同步在另一个组件中展示。也就是一个组件的数据值可以基于另一个组件的数据变化做出响应,这就是响应式。

在传统的命令式编程中,假设定义c = a*b,那么当a=1、b=2时,c的值就是2。之后a变量的改变不会引起c变量的变化,因为它们都是确定的。如果a、b的值是不确定的,即c=a*b,这个语句仅仅是定义了变量c与变量a、b的计算关系,那么c的值就是可变的。例如:

a=1,b=1,c=1

a=2,b=2,c=4

a=3,b=2,c=6

...

简而言之,c需要动态地由a、b共同来决定,当a、b的值发生变化时,c的结果需要及时地做出响应(或者叫反应),以此来保证正确性。变化的a、b相当于数据流,c要根据数据流的变化做出正确的响应,这就是Reactive Streams(响应式流)。

Java Flow API简介

基于Reactive Streams实现的响应式框架有RxJava、Reactor、Akka和Vert.x等。2017年,Java JDK 9发布,其中一个特性就是引入了基于Reactive Streams的Flow类。

Flow API是基于发布者/订阅者模式提供的推(push)和拉(pull)的模型,如图12.2所示。

基于发布/订阅模型的Flow更像是迭代器模式与观察者模式的组合。迭代器模式是拉(pull)模型,告诉数据源要拉取多少数据,观察者模式是推(push)模型,将数据推送给订阅者。Flow订阅者最初请求(拉)N个数据,然后发布者将最多N个数据推送给订阅者。

Flow类中定义了4个嵌套的静态接口,如表12.1所示。

下面介绍Flow的相关API,并给出一些实际的例子。

1. 基于Publisher与Subscriber的示例

Flow.Subscriber有4个抽象方法:

onSubscribe():发布者调用该方法异步传递订阅。

onNext():发布者调用该方法传递数据。

onError():发生错误时调用。

onComplete():数据发送完成后调用。

Sbscription的request()和cancel()方法提供的背压特性,让订阅者可以告诉发布者能接收的最大数据量,还可以取消订阅,这样不至于因发布者速度过快而导致订阅系统崩溃。示例如下:

public class PublisherAndSubscriberDemo {

public static void main(String[] args) throws

InterruptedException {

//发布者

SubmissionPublisher<String> publisher=new

SubmissionPublisher<>();

//订阅者

Flow.Subscriber<String> subscriber=new

Flow.Subscriber<String>() {

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);

try {

TimeUnit.SECONDS.sleep(2);

} catch (InterruptedException e) {

e.printStackTrace();

}

this.subscription.request(1);

}

//异常处理

@Override

public void onError(Throwable throwable) {

System.out.println("【订阅者】数据接收出现异常,"

+ throwable);

this.subscription.cancel();

}

//发送结束处理

@Override

public void onComplete() {

System.out.println("【订阅者】数据接收完毕");

}

};

publisher.subscribe(subscriber);

for (int i=0;i<5;i++){

String message = "hello flow api " + i;

System.out.println("【发布者】发布消息: " +

message);

publisher.submit(message);

}

publisher.close();

Thread.currentThread().join(20000);

}

}

控制台的打印结果如下:

【发布者】发布消息: hello flow api 0

【发布者】发布消息: hello flow api 1

【发布者】发布消息: hello flow api 2

【发布者】发布消息: hello flow api 3

【发布者】发布消息: hello flow api 4

【订阅者】接收消息: hello flow api 0

【订阅者】接收消息: hello flow api 1

【订阅者】接收消息: hello flow api 2

【订阅者】接收消息: hello flow api 3

【订阅者】接收消息: hello flow api 4

【订阅者】数据接收完毕

2. Processor示例

Processor扩展了Publisher和Subscriber,因此它可以在Publisher和Subscriber之间来回切换。Processor的示例如下:

public class MyProcessor extends SubmissionPublisher<Integer>

implements Flow.Processor<Integer, Integer>{

private Flow.Subscription subscription;

@Override

public void onSubscribe(Flow.Subscription subscription) {

System.out.println("Processor 收到订阅请求");

this.subscription = subscription;

this.subscription.request(1);

}

//传递数据

@Override

public void onNext(Integer item) {

System.out.println("onNext 收到发布者数据: "+item);

if (item % 2 == 0) {

this.submit(item);

}

this.subscription.request(1);

}

//处理异常

@Override

public void onError(Throwable throwable) {

this.subscription.cancel();

}

//结束处理

@Override

public void onComplete() {

System.out.println("处理器处理完毕");

this.close();

}

}

ProcessorDemo代码如下:

public class ProcessorDemo {

public static void main(String[] args) throws

InterruptedException {

SubmissionPublisher<Integer> publisher = new

Submission

Publisher<>();

MyProcessor myProcessor = new MyProcessor();

Flow.Subscriber<Integer> subscriber = new

Flow.Subscriber<>() {

private Flow.Subscription subscription;

@Override

public void onSubscribe(Flow.Subscription

subscription) {

this.subscription = subscription;

this.subscription.request(1);

}

//数据处理

@Override

public void onNext(Integer item) {

System.out.println("onNext 从Processor 接收到过

滤后的数

据 item : "+item);

this.subscription.request(1);

}

//处理异常

@Override

public void onError(Throwable throwable) {

System.out.println("onError 出现异常");

subscription.cancel();

}

//结束处理

@Override

public void onComplete() {

System.out.println("onComplete 所有数据接收完

成");

}

};

publisher.subscribe(myProcessor); //发布

myProcessor.subscribe(subscriber); //订阅

publisher.submit(1);

publisher.submit(2);

publisher.submit(3);

publisher.submit(4);

publisher.close();

TimeUnit.SECONDS.sleep(2);

}

}

最终打印结果如下:

Processor 收到订阅请求

onNext 收到发布者数据: 1

onNext 收到发布者数据: 2

onNext 收到发布者数据: 3

onNext 收到发布者数据: 4

处理器处理完毕

onNext 从Processor 接收到过滤后的数据 item : 2

onNext 从Processor 接收到过滤后的数据 item : 4

onComplete 所有数据接收完成

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值