谈谈我对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(后文统称为反应式编程)的时候问得最多的问题了,根据维基百科的定义:

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

首先反应式编程主要面向的是数据编程,而非面向逻辑编程。数据流在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 Programming是一种面向数据流的异步编程范式,通常应用在单个组件或者服务上。

  • Reactive System

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

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

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

Original: https://www.cnblogs.com/echooymxq/p/16247834.html
Author: 为你编码
Title: 谈谈我对Reactive Programming的理解

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/569938/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • IO(递归)

    递归:方法定义中调用方法本身的现象 注意事项:* A:递归一定要有出口,否则就是死递归* B:递归的次数不能太多,否则就内存溢出* C:构造方法不能递归使用 阶乘案例 不死神兔案例…

    Java 2023年6月5日
    078
  • 记录获取图片的api接口

    二次元 https://api.ixiaowai.cn/api/api.php?return=jsonhttps: https://uploadbeta.com/api/pictu…

    Java 2023年6月5日
    072
  • Gradle连载4-依赖包打包方式

    一、apply方法的使用 apply&#xA0;<span class="hljs-string">plugin:</span>…

    Java 2023年6月13日
    0100
  • 面向对象设计与构造2022第四单元总结

    一、总结本单元作业的架构设计 ​ 这里展示一下第三次作业的类图,相当于整个单元的架构设计。 ​ 首先,通过第四单元手册的学习,我们可以得知UML类图、顺序图和状态图每一种图它的元素…

    Java 2023年6月10日
    068
  • Mysql共享锁与排他锁

    mysql锁机制分为表级锁和行级锁,本文就和大家分享一下我对mysql中行级锁中的共享锁与排他锁进行分享交流。 共享锁又称为读锁,简称S锁,顾名思义,共享锁就是多个事务对于同一数据…

    Java 2023年6月5日
    078
  • JavaWeb-MVC、过滤器

    一、MVC架构图 Model 业务处理:业务逻辑(Service) 数据持久层:CRUD(Dao) View 展示数据 提供连接发起Servlet请求(a,form,img&#82…

    Java 2023年6月13日
    0100
  • Nginx 源码分析– 模块module 解析执行 nginx.conf 配置文件流程分析 二

    1 、获取全部参与编译的模块module 进行统计编号。 2 、根据module 模块的个数分配 配置信息资源的指针空间。 3 、创建NGX_CORE_MODULE 核心模块的配置…

    Java 2023年6月15日
    062
  • Spring Boot 面试问题

    说一说你对Spring Boot的理解 名词解释: Spring Boot 基于 Spring 开发, Spirng Boot 本身并 不提供 Spring 框架的核心特性以及扩展…

    Java 2023年6月7日
    066
  • Mybatis 一级缓存和二级缓存原理区别 (图文详解)

    Java面试经常问到Mybatis一级缓存和二级缓存,今天就给大家重点详解Mybatis一级缓存和二级缓存原理与区别@mikechen Mybatis缓存 缓存就是内存中的数据,常…

    Java 2023年6月15日
    078
  • String 对象

    String对象及底层区别 String 对象 两种方式 方式一:直接使用双引号得到字符串对象 代码: //方式一:直接使用双引号得到字符串对象 String name = &qu…

    Java 2023年6月6日
    061
  • 多线程

    Windows操作系统是多任务操作系统,它以进程为单位。每个独立执行的程序被称为一个进程,而每个进程又包含多个线程。系统可以分配给每个进程一段使用CPU的时间(CPU时间片),CP…

    Java 2023年6月5日
    067
  • Kubernetes-Namespace

    1. 简介 Kubernetes 支持多个虚拟集群,它们底层依赖于同一个物理集群。 这些虚拟集群被称为 namespace。 在一些文档里 namespace也称为 &#x…

    Java 2023年6月7日
    066
  • 基于寄存器与基于栈的虚拟机

    什么是虚拟机 虚拟机是借助于操作系统对物理机器的一种模拟。但是我们今天所讲述的虚拟机概念比较狭义,与vmware或者virtual-box不同,而是针对具体语言所实现的虚拟机。例如…

    Java 2023年5月30日
    081
  • 开篇说几句

    一直一览都是博客园的粉丝,从中查询了很多资料,给了我很多的帮助,随着时间的积累,自己有一些感悟和学习笔记,今天申请了个博客吧,吧自己的一些想法写出来,保留份记忆。 Original…

    Java 2023年6月8日
    065
  • MongoDb在windows10下的安装、创建用户和数据库

    1.mongodb下载地址https://www.mongodb.com/download-center#community 2.安装 3.在D:\MongoDB目录下创建db和l…

    Java 2023年6月16日
    071
  • Springboot+MDC+traceId日志中打印唯一traceId

    原文:https://www.jb51.net/article/225488.htm 目录 为什么需要这个traceId 2.通过MDC设置traceId 2.1 使用filter…

    Java 2023年5月30日
    083
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球