响应式编程实现技术概述

响应式编程就是利用异步数据流进行编程,本质上就是观察者(Observer)模式的一种表现形式。我们首先讨论实现异步操作的几种常见方式,然后引出响应式编程的主流实现技术。

1. 实现异步的常见方式

在Java中,为了实现异步非阻塞,一般会采用回调(Callback)和Future这两种机制,但这两种机制都存在一定局限性。

(1)回调

回调的含义如下图所示,即类A的methodA()方法调用类B的methodB()方法,然后类B的methodB ()方法执行完毕后再主动调用类A的callback()方法。回调体现的是一种双向的调用方式。

可以看到回调在任务执行过程中不会造成任何的阻塞,任务结果一旦就绪,回调就会被执行。但是我们也应该看到在使用回调机制时,代码会从一个类中的某个方法跳到另一个类中的某个方法,从而造成流程的不连续性。对于单层的异步执行而言,回调很容易使用。但是对于嵌套的多层异步组合而言就显得非常笨拙。所以回调很难大规模的组合起来使用,因为很快就会导致代码难以理解和维护,即形成所谓的“回调地狱(Callback Hell)”问题。

(2)Future

可以把Future模式简单理解为这样一种场景:我们有一个希望处理的任务,然后把这个任务提交到Future,Future就会在一定时间内完成这个任务,而在这段时间内我们可以去做其它事情。作为Future模式的实现,Java中的Future接口只包含如下5个方法。

public interface Future<V> {

    boolean cancel(boolean mayInterruptIfRunning);

    boolean isCancelled();

    boolean isDone();

    V get() throws InterruptedException, ExecutionException;

    V get(long timeout, TimeUnit unit)?

    throws InterruptedException, ExecutionException, TimeoutException;

}

 

Future接口中的cancel()方法用于取消任务的执行;isCancelled()方法用于判断任务是否已经取消;两个get()方法会等待任务执行结束并获取结果,区别在于是否可以设置超时时间;最后isDone()方法判断任务是否已经完成。

Future虽然可以实现获取异步执行结果的需求,但是它没有提供通知机制,我们无法得知Future什么时候完成。为了获取结果,我们要么使用阻塞的两种get()方法等待Future结果的返回,这时相当于执行同步操作;要么使用isDone()方法轮询地判断Future是否完成,这样会耗费CPU资源。所以,Future适合单层的简单调用,对于嵌套的异步调用而言同样非常笨重,不适合复杂的服务链路构建。

鉴于Future机制存在的缺陷,Java 8中引入了CompletableFuture机制。CompletableFuture一定程度上弥补了普通Future的缺点。在异步任务完成后,我们使用任务结果时则不需要等待。可以直接通过thenAccept()、thenApply()、thenCompose()等方法将前面异步处理的结果交给另外一个异步事件处理线程来处理。

CompletableFuture提供了非常强大的Future扩展功能,可以帮助我们简化异步编程的复杂性,并且提供了函数式编程的能力,可以通过回调的方式处理计算结果,也提供了转换和组合CompletableFuture所提供的各种方法。

对于日常的开发工作而言,大多数时候我们是在处理简单的任务,这个时候使用CompletableFuture确实可以满足需求。但是,当系统越来越复杂,或者我们需要处理的本身就是一个非常复杂的任务时, CompletableFuture对于多个处理过程的组合仍然不够便捷。使用CompletableFuture编排多个Future是可行的,但并不容易。我们会担心写出来的代码是否真的没有问题,而随着时间的推移,这些代码会变得越来越复杂和难以维护。为此,我们需要引入响应式编程的相关技术和框架,这些技术和框架能够支持在未来更轻松地维护异步处理代码。

2. 响应式编程的主流实现技术

目前,响应式编程的主流实现技术包括RxJava、Akka Streams、Vert.x和Project Reactor等。

(1)RxJava

Reactive Extensions(Rx)是一个类库,它集成了异步、基于可观察(Observable)序列的事件驱动编程,最早应用于微软的.NET平台。而RxJava是Reactive Extensions的Java实现,用于通过使用Observable/Flowable序列来构建异步和基于事件的程序库,目前有1.x版本和2.x版本两套实现。

RxJava 1.x版本诞生于响应式流规范之前,虽然可以和响应式流的接口进行转换,但是由于底层实现的原因,使用起来并不是很直观。RxJava 2在设计和实现时考虑到了与现有规范的整合,按照响应式流规范对接口进行了重写,并把1.x版本中的背压功能单独分离出来。但为了保持与RxJava 1.x版本的兼容性,RxJava 2在很多地方的使用也并不直观。关于RxJava的更多内容可参考官网(http://reactivex.io/)。

(2)Akka Streams

Akka运行在JVM上,是构建高并发、分布式和高弹性的消息驱动应用程序的一个工具套件。Actor是Akka中最核心的概念,它是一个封装了状态和行为的对象,Actor之间可以通过交换消息的方式进行通信。通过Actor能够简化锁及线程管理,可以非常容易地开发出正确的并发程序和并行系统。

Akka也是响应式流规范的初始成员,而Akka Streams是以Akka为基础的响应式流的实现,在Akka现有的角色模型之上提供了一种更高层级的抽象,支持背压等响应式机制。

(3)Vert.x

Vert.x是Eclipse基金会下的一个开源的Java工具,是一个异步网络应用开发框架,用来构建高并发、异步、可伸缩、多语言支持的Web应用程序。Vert.x就是为了构建响应式系统而设计,基于事件驱动架构,Vert.x实现了非阻塞的任务处理机制。

Vert.x中包含Vert.x Reactive Streams工具库,该工具库提供了Vert.x上响应式流规范的实现。我们可以通过Vert.x提供的可读流和可写流处理响应式流规范中的发布者和订阅者。

(4)Project Reactor

Spring 5中引入了响应式编程机制,而Spring 5中默认集成了Project Reactor作为该机制的实现框架。Reactor诞生较晚,可以认为是第二代响应式开发框架。所以它是一款完全基于响应式流规范设计和实现的工具库,没有RxJava那样的历史包袱,在使用上更加的直观易懂。但从设计理念和API的表现形式上,Reactor与RxJava比较类似,可以说Reactor基于响应式流规范,但在API方面又尽可能向RxJava靠拢。

Flux和Mono是Reactor中的两个核心组件,Flux代表包含0到n个元素的异步序列,而Mono则表示包含0个或1个元素的异步序列。

如果对文章感兴趣,可以关注我的微信公众号:程序员向架构师转型。

我出版了《系统架构设计:程序员向架构师转型之路》、《向技术管理者转型:软件开发人员跨越行业、技术、管理的转型思维与实践》、《微服务设计原理与架构》、《微服务架构实战》等书籍,并翻译有《深入RabbitMQ》和《Spring5响应式编程实战》,欢迎交流。

发布了92 篇原创文章 · 获赞 9 · 访问量 11万+

猜你喜欢

转载自blog.csdn.net/lantian08251/article/details/101520622