首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >从架构师的角度带你把“响应式编程”给一次性搞明白,果然绝绝子

从架构师的角度带你把“响应式编程”给一次性搞明白,果然绝绝子

作者头像
IT大咖说
发布于 2022-09-08 04:10:48
发布于 2022-09-08 04:10:48
5.1K0
举报
文章被收录于专栏:IT大咖说IT大咖说

◆ 响应式编程详解

响应式编程是一种基于异步数据流驱动、响应式、使用声明式范式的编程模型,需要遵循一定的响应式编程开发规范,并且有具体的类库实现。响应式编程基于数据流而不是控制流进行业务逻辑的推进。

◆ 响应式编程与设计模式

在面向对象编程语言中,响应式编程通常以观察者模式呈现。将响应式流模式和迭代器模式比较,其主要区别是,迭代器基于“拉”模式,而响应式流基于“推”模式。

在命令编程范式中,开发者掌握控制流,使用迭代器遍历“数据”,使用hasNext()函数判断数据是否遍历完成,使用next()函数访问下一个元素。在响应式编程模式中,使用观察者模式,数据由消息发布者(Publisher)发布并通知订阅者(Subscriber),而这种观察者模式本身在基于事件监听机制的响应式系统架构中被广泛使用。Java早期的Swing界面设计也是基于视图事件触发业务响应的系统工作模式。所以,从设计模式的角度讲,响应式编程并不是新鲜事物,只是响应式编程将监听的对象扩展到了更大范围:静态或者动态的Stream数据流,如下图所示。

响应式编程还借鉴了Reactor设计模式,我们通常会在高性能NIO网络通信框架中见到Reactor设计模式的身影,用来实现I/O多路复用。其基本思想是将所有要处理的I/O事件注册到一个中心I/O多路复用器上,同时主线程阻塞在多路复用器上,通过轮询或者边缘触发的方式来处理网络I/O事件。当有新的I/O事件到来或准备就绪时,多路复用器返回并将事件分发到对应的处理器中。Reactor设计模式和响应式编程类似,它们都不主动调用某个请求的API,而是通过注册对应接口,实现事件触发执行,如下图所示。

◆ 响应式编程与响应式架构

响应式编程很容易和响应式架构混为一谈。前面我们介绍了响应式宣言中的构建软件架构原则,把符合这些原则的系统称为响应式系统。如果说响应式系统与响应式编程之间具有什么关系,那就是响应式系统的架构风格是响应式的,而响应式编程是实现这个架构风格的最佳实践。从宏观角度看,响应式系统由各种不同组件相互操作、调用组成,共同响应用户请求。响应式系统涉及通信协议、I/O模型、网络传输、数据存储等多方面因素,保障系统在响应力、扩展性、容错、灵活性各方面表现出“实时”“低延迟”“轻量”“健壮”的系统特性。而响应式编程可能是这个大的系统架构下的一部分。另外,响应式系统一般是消息驱动的,而响应式编程是事件驱动的。

◆ 消息驱动与事件驱动

响应式宣言指出了两者的区别:“消息驱动”中消息数据被送往明确的目的地址,有固定导向;“事件驱动”是事件向达到某个给定状态的组件发出的信号,没有固定导向,只有被观察的数据。

  • 在一个消息驱动系统中,可寻址的接收者等待消息的到来然后响应消息,否则保持休眠状态,消息驱动系统专注于可寻址的接收者。响应式系统更加关注分布式系统的通信和协作以达到解耦、异步的特性,满足系统的弹性和容错性,所以响应式系统更倾向于使用消息驱动模式。
  • 在一个事件驱动系统中,通知的监听者被绑定到消息源上。这样当消息被发出时,它就会被调用,所以,响应式编程更倾向于事件驱动。

◆ 响应式编程与函数式编程

响 应 式 编 程 同 时 容 易 和 函 数 式 编 程 混 淆 。函 数 式 编 程(Functional Reactive Programming,FRP)在二十年前就被ConalElliott精确地定义了。在函数式编程中,函数是第一类(firstclass)公民,函数式编程由“行为”和“事件”组成。事件是基于时间的离散序列,而行为是不可变的,是随着时间连续变化的数据。函数式编程与响应式编程相比,它更偏重于底层编码的实现细节。

从Java 8开始,Lambda表达式的引入为Java添加了函数式编程的特性,函数式编程提供了闭包的强大功能。Java中的Lambda表达式通常使用(argument)->(body)语法书写,如下所示:

下面是一些典型的Lambda表达式及其函数式接口:

  • Consumer<Integer> c=(int x)->{System.out.println(x)};
  • BiConsumer<Integer, String>b=(Integer x, String y)->System.out.println(x+":"+y);
  • Predicate<String>p=(String s)->{s==null};

在Java 8中新增加了@FunctionalInterface接口,用于指明该接口类型是根据Java语言规范定义的函数式接口。Java 8还声明了一些Lambda表达式可以使用的函数式接口。下面是匿名类和使用函数式编程方式的对比示例。

首先,使用@FunctionalInterface定义一个函数式编程接口。

然后,分别使用内部类和Lambda表达式两种方式执行业务逻辑。

可以看到,在函数式编程中,Lambda表达式允许将一个箭头函数作为参数进行传递,这样的语法表达更加简洁,而本质上由编译器推断并帮助实现转换包装为常规代码。因此,可以用更少的代码来实现相同的功能。而响应式编程的重点是基于“事件流”的异步编程范式,响应式编程通过函数编程方式简化面向对象语言语法的臃肿。响应式编程解决问题的流程是:将一个大的问题拆分为许多独立的小的步骤,而这些小的步骤都可以异步非阻塞地执行;当这些小的子任务执行完,它们会组成一个完整的工作流,并且这个工作流的输入输出都是非绑定的。实现响应式编程的关键就是“非阻塞”,执行线程不会因为竞争一个共享资源而陷入阻塞等待,空耗资源,并且最大化地利用物理资源。

◆ 响应式编程与命令式编程模式

响应式编程是一种声明式的编程模型,与之相对应的就是命令模式(线程控制流)的编程模型。大家对命令式编程模式比较熟悉,下面是一段常见的基于命令式编程模式的代码:

上述代码是通过变量的赋值并通过加法计算响应数据之间的对应算数关系结果。但是,这个代码有一个潜在的问题,当我们给这两个变量重新赋值时,第二次的Sum值却没有变化,与我们的期望不符,原因是缺少了执行相加的命令指令。

响应式编程的目的是通过“不可变操作符”固定这种数据,构建数据之间的关系,并正确输出结果,不会因为操作命令的遗忘和缺失导致结果的偏差,造成对应关系和结果错误,下面我们看一下如何使用响应式编程方式来固化这种模式。

下面使用Java 9的Flow API实现两个数的相加功能,按照相同思路,当传入的变量不同时,输出的Sum值也会随着变化,我们把这种对应关系构建为一个声明公式,代码实现如下:

从结果看,响应式编程模式的两次Sum值和输入的数值一致,能够达到预期效果。从这个例子中,我们已经初步接触到了响应式编程中数据源也就是事件发布者(Publisher),还有就是事件的监听回调函数集合——消费者(Subscriber)。消费者会根据next、error、complet触发函数对应关系的执行,以及数据的操作符操作,由于消费者的不可变性,可以根据原生的数据结构生成新的数据结构。相比命令式编程,响应式编程使用操作符表述了一个通用业务执行逻辑,一般可以组合达到预期效果,一般的操作符还包含map、filter、reduce等函数,这里就不再赘述了。

◆ 编程范式

“普通的工程师堆砌代码,优秀的工程师优化代码,卓越的工程师简化代码”。

如何写出优雅整洁的代码,不仅是一门学问,也是软件工程的重要一环。在上一节中,我们简单介绍了响应式编程的编程范式,本节我们进一步从开发者的视角、系统的性能、满足用户需求等方面讨论不同编程范式的使用场景和特性优势。

编程范式,又称为编程模型,泛指软件编程过程中使用的编程风格,一般不同的编程范式具有不同的语法特性和差异。目前软件开发技术中常用的典型编程范式有以下几种。

  • 命令式编程。
  • 面向对象编程。
  • 声明式编程。
  • 函数式编程。

因为每一个编程范式都有很长的发展历史,在编程语言支持上有不同的标准、组织和语法规范等,本节的目的是希望通过对这些编程范式的介绍,可以帮助我们更好地理解响应式编程范式。

◆ 命令式编程

命令式编程是非常传统的软件编程方式,命令式编程由不同的逻辑执行步骤组成,通过一步步指令的执行达到业务逻辑的推进,这种方式也称为过程式编程。命令式编程的执行过程非常符合计算机的执行步骤。C语言是命令式编程的典型代表,它更关注的是机器域底层的内存、指令计算、输入输出。在C语言中,我们经常看到大段的过程式指令、各种if/else/for等控制语句、表达式、数据变量的操作、赋值等指令,这种纯指令开发方式要求开发者对计算机的底层工作原理有非常深刻的理解,而且一个指令出现偏差往往会产生不可预知的错误。同时,命令式编程模式的运维也是难度非常高的。

◆ 面向对象编程

面向对象编程可以说是编程领域的一个分水岭,开启了高级程序语言在软件开发上的统治阶段。面向对象编程从问题域出发,将封装、继承、多态的语言特性映射到我们的现实世界。在面向对象编程里,业务问题被抽象成类、接口模板,数据和行为被统一封装在对象内部,作为程序的基本组成单元。面向对象编程范式在提升软件重用性、灵活性和扩展性上比过程式编程更进一步,C++、Java作为面向对象编程语言的代表,屏蔽了机器底层的内存管理和机器域的管理细节。而面向对象编程虽然有较高的开发效率,但是降低了代码的运行效率,这也限制了面向对象编程在性能要求苛刻场景下的应用。

◆ 声明式编程

声明式编程受当前“约定优于配置”理念的影响,在软件编程开发领域中被大量应用。声明式编程范式的好处是可以通过声明的方式实现业务逻辑,不需要陷入底层具体的业务逻辑实现细节。声明式编程范式关注的焦点不是采用什么算法或者逻辑来解决问题,而是描述、声明解决的问题是什么。当你的代码匹配预先设定好规则,业务逻辑就会被自动触发执行。

很多标记性语言,如HTML、XML、XSLT,就遵循声明式编程范式,而Spring Boot基于注解方式的编程模型也是声明式编程的一个代表。

Spring框架依赖AOP和IoC编程思想降低了开发者对底层逻辑业务细节的了解程度。例如在Spring Boot中,通过@Transactional注解可以声明一个方法具备事务性的操作,当异常发生时,事务会自动回滚,保证业务逻辑的正常和数据一致性。发生在@Transactional注解背后的实现细节,开发者可以不去关心。

◆ 函数式编程

在函数式编程范式中,函数无疑是一等公民,函数式编程最具魅力或者最重要的特性就是不可变性。它的不可变性表现在函数式编程表达式的执行结果,只取决于传入函数的参数序列,不受数据状态变化的影响。

函数式编程中的Lambda在Java 8中被引入,可以看成是两个类型之间的关系:一个输入类型和一个输出类型。Lambda演算就是给Lambda表达式一个输入类型的值,它就可以得到一个输出类型的值。

这个计算过程也是函数式代码对映射的描述,因为函数式代码的抽象程度非常高,所以也意味着函数式代码有更好的复用性。

函数式编程和命令式编程相比,更加关注消息或者数据的传递,而不像命令式编程,关注的是指令控制流。共享数据的状态在多线程环境下会存在资源竞争的情况,往往我们需要把额外的精力投入到冲突地解决、数据状态的维护中。而函数的不可变性保证了数据在传递处理过程中不会被篡改,也不需要依赖外部的锁资源或者状态来维护并发。所以函数式编程在多核处理器中具有天然的并发性,可以最大化地利用物理资源实现并行处理功能。

目前,在JVM体系中,已经出现了越来越多函数式编程范式的语言,例如Scala、Groovy、Clojure等。在当前计算机多核、数据优先、高性能的诉求下,函数式编程具有更广阔的发展前景和未来。然而有利总会有弊,函数式编程的语法相比面向对象编程更晦涩,在大规模工程化的协调配合中,还是需要我们去权衡利弊。因为无论哪种语言范式,本质上都是工具,最终目的都是为业务服务。

来源:

https://www.toutiao.com/article/7138687931224441382/?log_from=1fe0ae5860d06_1662512946570

“IT大咖说”欢迎广大技术人员投稿,投稿邮箱:aliang@itdks.com

来都来了,走啥走,留个言呗~

 IT大咖说  |  关于版权

由“IT大咖说(ID:itdakashuo)”原创的文章,转载时请注明作者、出处及微信公众号。投稿、约稿、转载请加微信:ITDKS10(备注:投稿),茉莉小姐姐会及时与您联系!

感谢您对IT大咖说的热心支持!

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2022-09-07,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 IT大咖说 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
【数据结构】线性表和顺序表
椰椰椰耶
2024/09/20
1000
【数据结构】线性表和顺序表
数据结构 之 顺序表 ArrayList (Java)
常见的线性表有:顺序表(ArrayList),链表(LinkedList),栈(Stack),队列(Queue)...
AUGENSTERN_
2024/04/09
1360
数据结构 之 顺序表 ArrayList (Java)
ArrayList与顺序表
1.线性表(linear list)是n个 具有相同特性的数据元素的有限序列 。 线性表是一种在实际中广泛使用的数据结构,常见的线性表:顺序表、链表、栈、队列...
用户11305962
2024/10/09
1170
ArrayList与顺序表
顺序表的奥秘:高效数据存储与检索
小舒不服输
2024/01/31
2100
顺序表的奥秘:高效数据存储与检索
数据结构-2.顺序表
线性是n个具有相同特性的数据元素的有限序列. 线性表是一种在实际中广泛使用的数据结构,常见的线性表有: 顺序表 , 链表 , 栈 , 队列...
用户11369350
2024/11/19
640
数据结构-2.顺序表
《数据结构-第一章》之顺序表
线性表是一种线性结构。线性结构的特点是数据元素之间是一种线性关系,数据元素“一个接一个的排列”。在一个线性表中数据元素的类型是相同的,或者说线性表是由同一类型的数据元素构成的线性结构。线性表的逻辑上是连续的,物理上不一定连续的,比如数组它在逻辑上和物理上都是连续的,但是链表在逻辑上是连续的在物理上是不连续的。常见的线性表有数组,栈,队列,链表。
用户10517932
2023/10/07
2010
《数据结构-第一章》之顺序表
【数据结构】ArrayList与顺序表
---- 1.线性表 线性表(linear list)是n个具有相同特性的数据元素的有限序列。 线性表是一种在实际中广泛使用的数据结构,常见的线性表:顺序表、链表、栈、队列... 线性表在逻辑上是线性结构,也就说是连续的一条直线。但是在物理结构上并不一定是连续的,线性表在物理上存储时,通常以数组和链式结构的形式存储。 2.顺序表 2.1接口的实现 我们先自己来完成一个顺序表8:  具体效果如图: 源码如下: 建议小伙伴们自己思考一下上手敲一敲代码,对后续的学习可以更好的理解哟~ MyArr
xxxflower
2023/04/16
2120
【数据结构】ArrayList与顺序表
知识改变命运 数据结构【顺序表】
线性表(linear list)是n个具有相同特性的数据元素的有限序列。 线性表是一种在实际中广泛使用的数据结 构,常见的线性表:顺序表、链表、栈、队列… 线性表在逻辑上是线性结构,也就说是连续的一条直线。但是在物理结构上并不一定是连续的,线性表在物 理上存储时,通常以数组和链式结构的形式存储。
用户11319080
2024/10/17
1140
知识改变命运 数据结构【顺序表】
【数据结构】顺序表
前言: 小编在开始之前就已经发了顺序表的相关用例,想看的小伙伴可以去看看哦http://t.csdnimg.cn/saIbn
用户11288949
2024/09/24
920
【数据结构】顺序表
ArrayList部分功能实现
用户10921393
2024/01/23
1490
《Java初阶数据结构》----2.<线性表---ArrayList与顺序表>
2. ArrayList实现了RandomAccess接口,表明ArrayList支持随机访问
用户11288958
2024/09/24
1000
《Java初阶数据结构》----2.<线性表---ArrayList与顺序表>
手把手教你如何实现List——ArrayList
站在数据结构的角度来看,List就是一个线性表,即n个具有相同类型元素的有限序列,在该序列上可以执行增删改查以及变量等操作。
用户11369558
2024/11/20
1290
手把手教你如何实现List——ArrayList
java——顺序表的方法实现
顺序表需要有以下几点思考; 顺序表中间/头部的插入删除,时间复杂度为O(N) 增容需要申请新空间,拷贝数据,释放旧空间。会有不小的消耗。 增容一般是呈2倍的增长,势必会有一定的空间浪费。例如当前容量为100,满了以后增容到200,我们再继续插入了5个数据,后面没有数据插入了,那么就浪费了95个数据空间 顺序表的方法实现: import java.util.Arrays; public class MyArrayList{ public int[] array;//有效数组 public
小雨的分享社区
2022/10/26
4720
ArrayList与顺序表(1)
Java 中的 ArrayList(顺序表) 是集合框架中的一个类,要模拟实现顺序表,也得自己实现一个类,首先要考虑这个类中的成员属性。
E绵绵
2024/04/23
1130
ArrayList与顺序表(1)
Java顺序表
推荐一个网站给想要了解或者学习人工智能知识的读者,这个网站里内容讲解通俗易懂且风趣幽默,对我帮助很大。我想与大家分享这个宝藏网站,请点击下方链接查看。 https://www.captainbed.cn/f1
鲜于言悠
2024/05/28
1510
Java顺序表
【JAVA数据结构】(ArrayList)顺序表
ArrayList 是Java集合框架中的一个类,底层使用的数据结构就是顺序表,它实现了List接口,提供了动态数组的功能,ArrayList可以根据需要自动进行扩容(不指定容量大小也能正常添加元素),允许存储任意类型的对象。
用户11292525
2024/09/26
1530
【JAVA数据结构】(ArrayList)顺序表
【Java】ArrayList的模拟实现详解!!!
首先,我们知道ArrayList实现了List的接口,所以我们要知道List接口中有哪些方法,并且ArrayLiat要重写List接口中的方法这里我们对其是简单模拟ArrayList,我们实现其一些常见的功能就好。
喜欢做梦
2024/11/25
1490
【Java】ArrayList的模拟实现详解!!!
Java——数据结构之顺序表
  在之前的学习中,我们主要了解了很多 Java 的 基本语法,但是 在之后的 Java学习中,了解 基础 数据结构的知识 非常重要,数据结构的思想 可以帮助我们更加清晰 明白的了解 Java 的解题思路等等。
RAIN7
2021/08/11
8900
【数据结构二】手撕顺序表与ArrayList源码详解
对于任意一个继承list接口的数据结构我们都应该实现增删改查获取长度清空等方法,以及相应类的构造方法,我们知道Java中为了提高代码的复用,都是通过类继承接口的方式来进行代码试现,下面让我们写这样一个接口。
小皮侠
2024/04/08
1370
【数据结构二】手撕顺序表与ArrayList源码详解
【数据结构与算法】6.栈
栈:一种特殊的线性表,其**只允许在固定的一端进行插入和删除元素操作(表的末端)。**进行数据插入和删除操作的一端称为栈顶,另一端称为栈底。
爱敲代码的小杨.
2024/05/07
900
【数据结构与算法】6.栈
相关推荐
【数据结构】线性表和顺序表
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档