其他分享
首页 > 其他分享> > 谈谈我对Reactive Programming的理解

谈谈我对Reactive Programming的理解

作者:互联网

Microsoft于2012年的时候在.NET生态中实现了反应式扩展库,简称ReactiveX或Rx。跟着RxJava又开发了JVM上的实现。之后Pivotal、Netflix、LightBend和Twitter等厂商联合建立了Reactive Streams,并在2015-04-28发布1.0版本。并由Doug Lea通过JEP-266 More Concurrency Updates提案增加了Flow API包括了在JDK9中。

或许以前你没怎么听说过Reactive Programming,但随着Spring5的发布,WebFlux、R2DBC、RSocket等各种名词层出不穷,Reactive相关的文章不断,基于国内主要使用Spring全家桶居多,一下子Reactive Programming成为热门技术,似乎是Spring将Reactive Programming带到了新高度,但其实主流的微服务框架Helidon、Micronaut、Quarkus和Vert.x等都支持Reactive Programming。

什么是Reactive Programming?

这是在最开始学习Reactive Programming(后文统称为反应式编程)的时候问得最多的问题了,根据维基百科的定义:

反应式编程是一种面向数据流和变化传播的声明式编程范式。

https://en.wikipedia.org/wiki/Reactive_programming

看完介绍可能还是难以理解具体什么是反应式编程,因为它没有更详细的定义描述,没有结合具体编程语言进行讲解,反应式编程解决了什么问题?我们为什么要使用反应式编程?带着这些问题,从我自己的角度谈谈对反应式编程的理解。

首先反应式编程主要面向的是数据编程,而非面向逻辑编程。数据流在Java中对应Stream,涉及到流就会有过滤、转换、聚合和拆分,还有缓冲、调整流速率操作等等。变化传播类比观察者模式(Observer Pattern),当数据发生改变时,才会对其进行响应。另外变化传播也意味着异步非阻塞,Java使用Future表示异步任务结果,在获取执行结果时可能导致阻塞。而CompletableFuture能够真正的进行异步非阻塞操作,一旦任务完成触发回调执行下游方法,并可链接多个操作。

那么我的理解就是在Java中反应式编程(Reactive) = 数据流(Stream) + 异步(CompletableFuture) + 背压(BackPressure)。我们没办法使用Java的Stream和CompletableFuture很好进行数据流的异步编程。首先Stream使用Iterator进行数据读取,虽然提供了parallel并行操作,但是其终止操作依旧是阻塞的。Stream的数据是静态的,无法动态生成,另外缺少部分高阶操作如缓冲、窗口等。

Stream.of(1, 2, 3)
        .parallel()
        .map(String::valueOf) // paralleled
        .collect(Collectors.toList()); // blocked

CompletableFuture支持异步链式回调,并可指定Executor对应执行线程池,虽然提供了allOf方法,但无法对多个(流)异步操作进行组合处理。anyOf为当其中一个操作完成时结束,不能中断其它未完成的操作。

CompletableFuture
        .allOf(f1, f2, ...)
        .thenApply(tt -> {
            // where my result?
            // need result? 
            Object result1 = f1.join();
            Object result2 = f2.join();
        });
CompletableFuture
        .anyOf(f1, f2, ...)
        .thenApply(tt -> {
            if (!f2.isDone()) {
                f2.cancel(true); // CompletableFuture cancel not working.
            }
        });

BackPressure指上游生产者的生产速率大于下游消费者的消费速率时的流控处理。一种是消费者根据自身能力使用Pull的方式进行数据获取;另一种则是生产者调整流控,当消费者来不及消费时,将数据进行缓冲、丢弃等。

那么反应式编程解决了什么问题呢?我们为什么要使用反应式编程?我们以Tomcat为例,Tomcat默认采用一个请求一个线程的方式,假设配置的线程池大小为100,响应时间为100ms/r,那么系统的QPS能够达到1000r/s。假定在固定线程池大小不变的情况下,因为更多的线程意味着更多的上下文切换和内存消耗,如果因为一些阻塞操作如IO等导致线程阻塞,加大了响应时间,那么整体系统的QPS将降低。另外通常我们的业务系统的基本流程为请求(解析Json) -> 数据交换(Database) -> 响应(组装Json),而我们可以使用反应式编程,把请求当作数据流,基于数据流,根据反应式编程框架提供的操作符,声明式的组装业务流程和逻辑,并且包含高阶的并发抽象,异步编程会更容易,更充分地利用系统资源。我们此处暂不论Servlet异步API,因为它同样面临上面提到的异步编程困境。

反应式编程不是银弹,它的缺点?

Reactive Programming和Reactive System的区别?

Reactive Programming是一种面向数据流的异步编程范式,通常应用在单个组件或者服务上。

Reactive System 则是系统级别的,比如分布式系统。反应式宣言描述了符合反应式系统的设计原则,其中包括Responsive、Resilient、Elastic和Message Driven几个特性。

总结

Reactive Programming是非常有意思的,并且值得所有开发人员学习,但是你可以在工程上完全不使用Reactive Programming,这取决于你的系统能够从中获取什么,并承受相应带来的弊端。但是例如在一些负载均衡代理方面的应用,Reactive Programming有极大的优势,或者涉及到一些数据流编排,如多个微服务API链式调用,Reactive Programming能够很轻松的表达异步调用。

Reference

想看更多此类文章的可以关注我的公众号,微信搜索【为你编码】

标签:异步,编程,Programming,Reactive,谈谈,反应式,数据流
来源: https://www.cnblogs.com/echooymxq/p/16247834.html