eactor响应式编程

像这样围绕着数据流转的程序,我们可以简称为执行序列。应用响应式编程最直接的方式就是从以往的声明式程序中找出核心数据,或者说是抽象出核心数据,然后把程序的执行过程转变为对核心数据的执行序列,最终,如果是查询数据的程序,就返回当前的执行序列如果是执行命令的程序,则可以返回当前序列的后续(then())。

为什么不直接返回生成好的数据,而要返回但前的执行序列呢?原因有如下几点(可能还有其他的,但是暂时没想到)

有个微服务的最佳实践模式命令查询职责隔离模式(CQRS),在该模式当中,将客户端的请求分为两类:

在响应式编程设计中,我们也可以使用相同的概念。

该模式还有一个概念,叫做事件溯源(Event Sourcing),服务和服务之间除了命令和查询之外,还有事件,和响应式编程的模式非常契合,我也是因为之前学习过这个模式,才被启发想到需要给API分门别类的。

响应式编程的事件类型是固定的,通过调用接口去传递事件(next事件,error事件,subscribe事件,complete事件,事件也可以称之为状态,其实都是一回事儿,只不过抽象方法以及场景不一样,所以有不同的叫法)

简单一点说,所有返回类型为 void 的接口都是命令式接口。

一般这样的接口,使用响应式变编程的思维,需要返回一个内部执行序列的后续,所以说,所有void方法的返回值可以统统是Mono<Void> ,该对象代表了该接口提供的针对于传入参数的后续执行序列。

简单一点说,所有返回类型不是 void 的接口都是查询接口。

一般这样的接口,需要区分一下返回的是单个数据,还是一组数据,如果是单个数据,可以使用Mono<T>来表示,如果是一组数据,可以使用Flux<T> 来表示,这两个对象代表了该接口提供的针对传入参数的数据查询操作的执行序列。

其实查询操作使用数据发布者的角色来考虑更好理解:该接口返回了针对于传入参数的数据发布者。调用者可以通过订阅该发布者去获取数据。

简单一点说,所有不直接传递参数值,而是提供一个参数的容器,或者把参数封装成输入,并且返回响应的容器或者输出的接口,都是拼接接口(和map操作有点像)。

响应式编程更注重数据的流转,那么,谁才是数据的发布者,谁又是数据的订阅者呢?当然这个问题会随着业务逻辑的不同而有所变化。发布者也可以是其他发布者的订阅者,订阅者也有可能是其他订阅者的发布者,这样就会形成一个链条,将不同的组件链接起来,实现某些业务。

通常的,我认为,每个独立的组件或者模块,不论大小,在响应式编程的设计思路下,都需要抽象出内部的数据发布者,和数据订阅者,其实就是输入和输出(Input and Output),嗯,这是应该是很正常的事情。发布者和订阅者也是输入和输出的其中一种抽象方式。

这里想再多说一点关于输入和输出的话题,个人感觉,任何程序,不论粒度的大小(代码块,函数,类,模块,组件,服务,中间件,框架等等)在进行设计的时候都需要考虑这段程序的的输入是什么,输出是什么,以及输入和输出的方式,函数自然不用说,就是传参和返回值,想要改变这种简单的输入输出模式,只能通过使用某些模式来抽象参数和返回值的方式来扩展输入输出的功能。这里说到输入,响应式编程,还有另外一种思考方式,上下游 ,传递参数的时候,直接给一个明确的值,当然也可以传递某些执行序列的上游信息,然后方法内部实现是提供针对某些执行序列的下游执行序列,然后返回一个新的执行序列。Reactor3中的执行序列,当你在添加下游执行单元的时候,会返回一个新的执行序列对象,其中包裹着上游执行序列对象,内部称之为source,有点像是装饰者模式,没有太深入看源码,所以不太确定。

所以除了命令查询两种方法之外,还有另一种,应该叫做 拼接 方法(我自己起的名字)。拼接式接口就像上边所说的,传一个上游序列,拼接新的执行单元,然后返回新的执行序列,方便复用。

在使用Spring WebFlux 实现websocket的时候有个WebSocketHandler就和这个拼接方法类似,不过那个是热序列的拼接,比较不好理解。

handle方法传进来的是个WebSocketSession对象,调用receive()方法返回的是一个Flux<WebSocketMessage>对象,我们该如何理解这个Flux对象呢?

可以理解为一个执行序列的上游信息,我们可以在其基础之上添加相应的执行单元,因为这是个WebSocketSession,所以其实是个热序列(会不断生成新数据,直到断开链接),所以我们定义的下游执行单元需要指定,当收到新增数据的时候,我们应该执行什么操作,这也是个典型的push模式,这里调用doOnNext()方法传递一个Consumer对象其实是在定义Subscriber的onNext()方法在收到新数据的时候需要执行的操作,用发布订阅的模式去理解这个过程就是,参数传递进来的是个数据发布者,方法内部的程序是数据订阅者,订阅者要向发布者订阅数据,但是不是真正的订阅,而只是提供一个订阅之后的执行逻辑,因为从整体来说的话,该方法只是其中的一个执行单元,如果在这里调用subscribe方法,那后续再添加任何执行单元就都没有什么用了。所以这里其实是有个原则的。

序列的开始,是由订阅者调用发布者的subscribe方法,首先publishier会创建一个subscription对象,并回调订阅者的onSubscribe方法,将subscription注入到订阅者的体内,当订阅者需要从发布者那里获取数据的时候,订阅者可以调用Subscription对象的request方法,发布者生成指定数量的数据之后,循环调用subscriber对象的onNext方法用来传递数据,当发布的过程产生错误,发布者会调用订阅者的onError方法来传递错误,当发布者发布完成,会调用订阅者onComplete方法代表发布完成了,之后,发布者将不会再次发布数据,这个流程就结束了。

以上是标准的冷序列的调用直径流程。热序列和这个流程有点不一样并且不太容易理解,所以我们到后续的章节再单独进行说明。

发布订阅模式可能更多的是应用于进程之间或者线程之间的交互,其中通过阻塞队列或相应的消息中间件去实现消息的容器,但是在响应式编程当中,该模式的主要作用,就是进行连接,将数据从一个组件传递到另外一个组件,而且默认是没有像阻塞队列那样的数据容器,除非手动指定buffer模式,后边介绍各种Operator的时候会说到buffer模式。

以发布订阅模式的出发点去思考的话,应对理解响应式编程有帮助。

一个数据提供者对象,都能提供哪些数据,这些数据通过什么方式传递?

一个可执行命令的对象,都能提供哪些命令的执行?这些命令什么时候去执行,我能否先获得所有需要执行的命令,再根据一些规则,去定制他们的执行顺序?

THE END
0.这五款智能黑科技产品别犹豫,趁双十二赶快上车!|蓝牙耳机|充电器|除此之外,AirPods Pro还有一个大亮点,那就是锁耳设计,可以在运动或者奔跑时,不会出现掉落的情况。 苹果蓝牙耳机一直都是行业的领导者,技术永远被模仿,但无法被超越。AirPods Pro强大的科技体验感,目前双十二期间到手1799元,用它送女朋友在再适合不过了。 荣耀智能手表2代 荣耀智能手表2代分为碳石黑、亚麻棕、玛瑙黑、樱粉jvzquC41f{428<3eqo5bt}neng5GVAFK7W:17:6C87>/j}rn
1.Qt多线程1:QThreadqtqthread如果QThread是在ui所在的线程里生成,那么QThread的其他非run函数都是和ui线程一样的,所以,QThread的继承类的其他函数尽量别要有太耗时的操作,要确保所有耗时的操作都在run函数里。 在UI线程下调用QThread的非run函数(其实也不应该直接调用run函数,而应该使用start函数),和执行普通函数无区别,这时,如果这个函数要对jvzquC41dnuh0lxfp0tfv8my74911jwvkerf1mjvckrt1:83249:39
2.2022前端面试题mobx面试题路由分为前端路由和后端路由,后端路由是服务器根据用户发起的请求而返回不同内容,前端路由是客户端根据不同的URL去切换组件;在web应用前端开发中,路由系统是最核心的部分,当页面的URL发生改变时,页面的显示结果可以根据URL的变化而变化,但是页面不会刷新。 react生态中路由通常是使用react-router来进行配置,其主要jvzquC41dnuh0lxfp0tfv8}kcqjfd~kw{ct0c{ykenk0fnyckny03;:292793
3.iOS10适配汇总有点晕,不是么?一个开发者很难在不借助于文档的帮助下区分 application(_:didReceiveRemoteNotification:) 和 application(_:didReceiveRemoteNotification:fetchCompletionHandle:),新入行的开发者也不可能明白 registerForRemoteNotificationTypes 和 registerUserNotificationSettings(_:) 之间是不是有什么关系,Remote 和 LocjvzquC41dnuh0lxfp0tfv8jcpmngwlngkhmqp4ctvodnn4fgvgjn|4747>43?5
4.[前端面试]vue八股前端八股vmodel比如说:我们在data中声明了一个对象person,但是在后期为person增加了新的属性,那么这个新的属性就会失去响应性。想要解决这个问题其实也非常的简单,可以通过Vue.$set方法来增加指定对象指定属性的响应性。但是这样的一种方式,在Vue的自动响应性机制中是不合理。 jvzquC41dnuh0lxfp0tfv8vsa99399<421gsvrhng1jfvjnnu1756957:5;
5.QT学习使用多线程的两种方法(子类化QThread+子类化QObject)本文的重点不是教会你继承run写一个多线程,任何有编程基础的5分钟就能学会使用QThread的方法,本文真正要讲的是后面那几节,如如何安全的退出一个线程,如何开启一个临时线程,运行结束马上关闭等问题。如果你对QThread有初步了解,那么可以略过这节,但你最好看看这节后面提出的几个问题。 jvzquC41dnuh0lxfp0tfv8z234984>=61cxuklqg1fkucrqu1:662B739