响应式编程?
其实说白了,和传统的编程方式没什么区别,都是一些函数调用,只不过是增加了很多适配模式等。
响应式编程的英文名,Reactive Programming,那就是针对响应的呗。那啥叫响应呢?你烧水呢,水烧开了,水壶会叫,这就是一下响应了。不要想的太复杂,这些东西都是基于现实世界的需要而来的。
响应式它是依赖于事件的,响应式的代码它的运行不是按代码的顺序,而是跟多个按时间发生的事件有关。可能你会想,依赖事件?这不就是“回调”嘛,但在响应式编程里,这些按时间排列的事件,被称为“流”,stream。
简单的讲,响应式中的事件序列类似于js的数组,它里面的事件流就是时间的序列。
响应式编程,就是异步的数据流的开发。
响应式编程,它的关注重点在于“大量的UI事件与数据的互相影响”。啥意思呢,就例如某篇文章,你点个赞,那么一、所有其它人能看到赞;二、作者本人赞数量增加;三、文章权重提升;四、作者排名可能变化;。。。更多,“一个数据的变化,它的影响可能是呈现网状扩散”。
它的特点吧,一是速度响应快,低延迟;二是健壮性弹性,有故障也能尽量响应;三是资源弹性,访问量大自动加资源,少了自动减;四是有消息自动传递。
响应式的思想,实际是观察者模式 + (stream与事件源的通信控制)。
函数式编程?
函数式编程是一系列被不公平对待的编程思想的保护伞,它的核心思想是,它是一种将程序看成是数学方法的求值、不会改变状态、不会产生副作用(后面我们马上会谈到)的编程方式。
FP 核心思想强调:
声明式代码 —— 程序员应该关心是什么,让编译器和运行环境去关心怎样做。
明确性 —— 代码应该尽可能的明显。尤其是要隔离副作用避免意外。要明确定义数据流和错误处理,要避免 GOTO 语句和 异常,因为它们会将应用置于意外的状态。
并发 —— 因为纯函数的概念,大多数函数式代码默认都是并行的。由于CPU运行速度没有像以前那样逐年加快((详见 摩尔定律)), 普遍看来这个特点导致函数式编程渐受欢迎。以及我们也必须利用多核架构的优点,让代码尽量的可并行。
高阶函数 —— 函数和其他的语言基本元素一样是一等公民。你可以像使用 string 和 int 一样的去传递函数。
不变性 —— 变量一经初始化将不能修改。一经创建,永不改变。如果需要改变,需要创建新的。这是明确性和避免副作用之外的另一方面。如果你知道一个变量不能改变,当你使用时会对它的状态更有信心。
函数式编程:JS、Scala、Erlang
响应式编程与函数式编程的区别?
它和函数式编程的区别,这个简单的说一下,函数式编程就是二个字,“不变”。啥都不变,一经创建永远不变。如果要变,再创建个新的。在它里面函数就是数据的通道。参数确定时,结果是可以预测的。
##更进一步
怎么理解响应式的背压?
可以理解为承上启下。比如洪水,大坝的作用就是背压。背压应该写在靠近生产者的地方,或者说是连接元素生产者和消费者的一个地方,即生产者和消费者的连线者。背压应该具有承载元素的能力,也就是其必须是一个容器的,而且元素的存储与下发应该具有先后的,可以使用队列来实现。
打一个比喻:去电影院看电影,我们作为消费者消费电影,电影厂商提供生产电影,电影院负责下发电影,电影院作为生产者与消费者之间的连线者,我们不需要关心电影院会放映多少电影,我们只需要观看就行了。
背压可以平衡请求或响应率,这点与异步机制区别所在,也就是说,当响应堵塞时,会同时堵塞请求,因此reactive响应式=异步+同步(背压)。
背压是一种通过传输(通知)接收者可以消费多少元素来调节生产的机制(消费决定生产); 在这里,我们有一个棘手的问题。TCP具有字节抽象而不是逻辑元素抽象。我们通常所说的背压控制是控制向网络发送/接收的逻辑元件的数量。即使TCP有自己的流控制,这个流控制仍然是字节而不是逻辑元素。
https://www.jdon.com/50267
使用RSocket协议的公平背压
为了通过网络边界实现逻辑元素背压,我们需要一个适当的协议。幸运的是,有一种称为RScoket协议。RSocket是一种应用程序级协议,允许通过网络边界传输实际需求。该协议有一个RSocket-Java实现,允许设置RSocket服务器。在服务器到服务器通信的情况下,相同的RSocket-Java库也提供客户端实现。
对于浏览器 - 服务器通信,有一个RSocket-JS实现,能通过WebSocket连接浏览器和服务器之间的流通信。
https://stackoverflow.com/questions/52244808/backpressure-mechanism-in-spring-web-flux/52245213?stw=2#52245213
怎么理解阻塞和异步?
咖啡很烫,不能喝,只能等到冷却,这个冷却就作为了阻塞,因为其占据了一条线程,但我们可以看电视,也就是说你阻塞你的 ,但是我仍然可以看电视。看电视是主线程。于是异步就出现了,涉及到异步,就涉及到线程的状态。
既然阻塞要占据一条线程来工作,那么必然会有线程状态的管理。
非阻塞更多体现在等待层面,现实中我们等一个人,往往都是由时间限制的,难道我们要用一生的时间去街口等待一个人而荒废一生?总有跳出阻塞的路子。在nio里就是timeout。
我们讲的非阻塞都是相对的,具体某个任务该阻塞还是得阻塞。
Socket下同步/异步和阻塞/非阻塞:
同步/异步是属于操作系统级别的,指的是操作系统在收到程序请求的IO之后,如果IO资源没有准备好的话,该如何响应程序的问题,同步的话就是不响应,直到IO资源准备好;而异步的话则会返回给程序一个标志,这个标志用于当IO资源准备好后通过事件机制发送的内容应该发到什么地方。
阻塞/非阻塞是属于程序级别的,指的是程序在请求操作系统进行IO操作时,如果IO资源没有准备好的话,程序该怎么处理的问题,阻塞的话就是程序什么都不做,一直等到IO资源准备好,非阻塞的话程序则继续运行,但是会时不时的去查看下IO到底准备好没有呢;
我们通常见到的BIO是同步阻塞式的,同步的话说明操作系统底层是一直等待IO资源准备直到ok的,阻塞的话是程序本身也在一直等待IO资源准备直到ok,具体来讲程序级别的阻塞就是accept和read造成的,我们可以通过改造将其变成非阻塞式,但是操作系统层次的阻塞我们没法改变。
我们的NIO是同步非阻塞式的,其实它的非阻塞实现原理和我们上面的讲解差不多的,就是为了改善accept和read方法带来的阻塞现象,所以引入了Channel和Buffer的概念。
这里有一个形象的比喻:
如果你想吃一份宫保鸡丁盖饭:
同步阻塞:你到饭馆点餐,然后在那等着,还要一边喊:好了没啊!
同步非阻塞:在饭馆点完餐,就去遛狗了。不过溜一会儿,就回饭馆喊一声:好了没啊!
异步阻塞:遛狗的时候,接到饭馆电话,说饭做好了,让您亲自去拿。
异步非阻塞:饭馆打电话说,我们知道您的位置,一会给你送过来,安心遛狗就可以了。
“一个IO操作其实分成了两个步骤:发起IO请求和实际的IO操作。
同步IO和异步IO的区别就在于第二个步骤是否阻塞,如果实际的IO读写阻塞请求进程,那么就是同步IO。
阻塞IO和非阻塞IO的区别在于第一步,发起IO请求是否会被阻塞,如果阻塞直到完成那么就是传统的阻塞IO,如果不阻塞,那么就是非阻塞IO。
同步和异步是针对应用程序和内核的交互而言的,同步指的是用户进程触发IO操作并等待或者轮询的去查看IO操作是否就绪,而异步是指用户进程触发IO操作以后便开始做自己的事情,而当IO操作已经完成的时候会得到IO完成的通知。而阻塞和非阻塞是针对于进程在访问数据的时候,根据IO操作的就绪状态来采取的不同方式,说白了是一种读取或者写入操作函数的实现方式,阻塞方式下读取或者写入函数将一直等待,而非阻塞方式下,读取或者写入函数会立即返回一个状态值。
所以,IO操作可以分为3类:同步阻塞(即早期的IO操作)、同步非阻塞(NIO)、异步(AIO)。
同步阻塞:
在此种方式下,用户进程在发起一个IO操作以后,必须等待IO操作的完成,只有当真正完成了IO操作以后,用户进程才能运行。JAVA传统的IO模型属于此种方式。
同步非阻塞:
在此种方式下,用户进程发起一个IO操作以后边可返回做其它事情,但是用户进程需要时不时的询问IO操作是否就绪,这就要求用户进程不停的去询问,从而引入不必要的CPU资源浪费。其中目前JAVA的NIO就属于同步非阻塞IO。
异步:
此种方式下是指应用发起一个IO操作以后,不等待内核IO操作的完成,等内核完成IO操作以后会通知应用程序。”