写点什么

反应式编程与土豆炖肉

用户头像
superman
关注
发布于: 2020 年 06 月 21 日
反应式编程与土豆炖肉

1:分析方法

内容涉及主要概念

函数编程,回调,异步编程,非阻塞流,事件驱动,消息驱动,反应式编程

参照现实生活理解

程序解决问题过程就是完成某个任务,也可类比按现实世界人类完成某个任务过程。

程序处理过程核心概念:数据,线程,函数,三者的配合分解串联

人完成某个任务:物,人,活(处理方法)

对应关系为:物-数据,人-线程,活-函数

解决问题方法

解决复杂问题(大任务)就是把问题分解为若干小问题(小任务),调配执行者(线程,人)去配合完成。问题越复杂,任务的拆分,组合,人力调配过程就越重要,直接决定任务能否正确完成及完成的效率高低。

这个过程对应到程序就是程序的子任务拆分与协助方法。

解决问题(任务)的复杂性演化推动程序设计方法的演化,对应出现解决某类特性问题相适应的设计方法。

 

就以在家媳妇安排自己做一顿饭为任务,来分析场景演化与对应方法。

2:子任务拆分协调的编程模型

1:顺序调用

生活场景

第一次安排做土豆炖肉--逐步安排

1》给你葱去切丝

2》给你土豆去切块

3》给你肉去切块

4》把放葱,土豆,肉 入锅 炖

特点:一步步的安排--单人按顺序逐步执行

物:土豆,葱,肉

执行人:自己

活(任务)拆分:切土豆,切葱,切肉,炖(材料放入,加火炖)。

编程模型

面向过程执行。

程序处理简单任务的过程一般比较符合,把处理过程拆分为几个函数,逐步将数据给函数处理。

 

2:流水线模式

生活场景

给你任务单,你按顺序做,以后每次都这个顺序做。

1>任务拆分到任务单:切葱,切土豆,切肉,炖肉

2>给你指定任务单(工作流程),材料去按照这个顺序做土豆炖肉

任务单上的任务遵循一定的行为规范,比如输入输出相同或可以拼接各自输入输出(上一个输出是下一个输入)

3:什么时候需要吃了,就把料给你,你就按这个顺序做。

好处:把物与任务单一次给你,中间步骤不用操心,一次交代,后续源源的任务过来都按这个步骤做。

任务单上的工种可局部替换,可添加。灵活定制各种口味

编程模型

java 的流处理模型,函数链(比如 Netty 的 Pipeline 模型的责任链模式)

netty 的 pipleline 用相同类型的工序组织。

java 流本质就是函数链,抽象出几种类型工种,可方便组装流水线。某个类型工种的具体做法可定义。

区别点:java 的流是拉的,只有最后收集点才会触动流水线开工,java 流的并发模式支持中间某个工序用多个人。

用途

1>对符合流水线工序要求的基础上灵活拼接多个,方便将一个大任务分解为一系列按步骤处理的子任务。

Netty 的 Pipleline 添加 handler 就是典型例子,可以将编码,心跳检查,加解密,安全认证等组装起来,而每一个工段只关注自己的一件事。

把土豆变成炒菜的土豆块:洗,削皮,切块,每一道任务都很专一,上一道任务结果给下一道处理。

将来需要切丝,只把最后一道工作替换,需要把土豆有芽的扔掉,就再最前面加一道挑拣工序。

需要将不规则土豆在削皮前切成规则大块,就在削皮前加一道工序。可以不断精细优化。

2>部分工作重用,重用总体工序(一种步骤限制),重用其中某个工段。

总体工序

如:定义切土豆的流水模式:洗,削皮,切。 

工序是通用固定的,但其中某些工段可个性设置。比如根据喜好可以更换洗,削,切的个性手法

一般在框架上定义好工序,具体工段应用个性注入。

重用工段

切块机,什么固态东西放入都给切成要求的块。那肉,土豆都可以用。


关键点:定义流水线,按顺序执行,个性配置流水线中的工段

1:场景 1- 任务切分为多个工段,可以一次交给一个 worker,按顺序做完。(不同工段的处理工序有专门的函数定义,netty 的 pipleline 模型),工序可以方便进行扩展,精细化(每个工段只完成相对单一的功能),扩展性更好(添加就可以完成扩展,而不用修改)

2:任务切分为多个工单,同样任务源源不断过来,多个 worker 分别负载不同的工段(工厂的流水线,计算机 CPU 的指令处理)-分工协作

3:为了节省交互,一次交给的是有顺序的多个任务(较少交互的消耗-批量有序),对执行者类似(http1.1 支持 pipline 模式,客户端提交多个请求,redis 支持 pipline 模式,一次提交多个命令,服务端按顺序返回多个响应-减少网络交互次数)

饭馆里:和面,拉面,煮面,卖面,洗碗 由不同工种各种负载自己的,整体形成流水线 模式。-解耦(功能解耦效率高-拆分功能)


3:函数编程

生活场景

给你一个炒土豆炖肉机

炒菜你不会炒(难吃),你就做切的活(粗活),给你炒菜机,切好用机器炒。炒菜机只要你把材料按要求放进去,按按钮就行了。

 

科技发达后续可能给你几个机器,切肉机,切土豆机,炒土豆炖肉机,你分别按顺序操作就行。

还可以更发达,炒菜机可以灵活组装,包括切土豆,切肉的机器接口

你把切肉机,切土豆机连上炒菜机,把土豆,肉放进去,摁按钮就出来土豆炖肉了

 

编程模型

函数编程,将函数像数据一样传入,方法内部直接调用传入的函数。具体函数实现不用了解。

函数也可用由其他函数组装(炒菜机有切肉机接口)

函数编程与回调区别

函数编程是可在执行中间用,也可传递若干函数,函数也可以由其他函数装配。

回调一般是最后调用-炒菜机,并且传递一个,更多用于执行完后触发另一个函数,回调是函数编程的简化版(不能组装,最后调)。

 

函数也可能是闭包(炒菜机里面内置辣椒酱),如何炒跟口味你都控制不了。

 

4:异步编程

生活场景

你先忙,干完叫我

还是炒菜,这次媳妇把任务跟材料都给你,让你做完通过某种方式告诉她。交代完就走了,在你做菜期间她可以做别的重要的事(刷碗,刷微博,刷抖音)。

编程模型

异步编程,

交给专业线程做。可以是线程池,可以用协程(go),或交给其他服务,交给系统。

分两种情况:关注结果--菜做完了要给她(还没吃),不关注结果(她吃过了)

不关注结果:直接丢给新线程做就完了。

关注结果:要求有做完后的通知机制。

如何确认任务完成呢:

1>轮询(多问问好了没),

2>等通知(做好了叫她-回调),

3>消息|事件队列(做好了放到固定位置-桌子上,她有空了去桌子上看(事件列表,消息列表)好没,好了就吃

4>阻塞等待,等线程执行完毕后在继续执行,没事或急需要执行结果时采用。(啥也不干就干等,虽然她不累但也没干别的事,饿了就会这么干,吃饭前啥也不做)。

 

Java--可用 Callback 接口的 Feature .GO 可用传递一个 channel.也可以完成后用其他消息模式通知。这也是很多异步框架做的事(可用有专门线程监控消息,或轮询,完成后通知给关注者)。

JAVA NIO ,linux epoll 模式相当于有一个线程监控多个连接的完成情况(专人轮询)。

 

5:MQ,任务队列

生活场景

发挥主人翁精神,主动要活,不累坏,也不偷懒

媳妇可能有很多任务安排,做饭,洗衣服,扫地等。都给你怕你累着(体贴人),就把需要的任务放到列表上,想起一个放一个,你就发挥主人翁精神,去逐个领取,完成一项,马上再去取下一个执行,没就等活。

即便你不在或忙别的,一会来了按着做就是,或孩子帮你完成也可以,媳妇不用等。


解耦了任务分配方与任务执行方。

不用等待任务执行完毕,也不用怕你累着,需要的时候可以针对性的扩容消费者


编程模型

MQ,任务队列

用消息队列做流量削峰,线程池去任务队列领任务都有这层意思,这要求任务间是相对独立的,没有严格的次序依赖(不需要上一个任务的结果),拉任务可以是本进程内(线程池任务队列),也可以使网络上(MQ)


同时也是一种架构模式:可减少系统间的依赖,但要求是上游不依赖下游的执行结果.

消费者临时故障,也不影响生产者。

6:消息驱动,事件驱动


生活场景

如果家里没家务活你不用一直干等吩咐,可以干别的事,媳妇有活告诉孩子,孩子告诉你

孩子自己有要求也告诉你。收到后处理。


媳妇说下午去公园玩,你收到后,去准备水,水果,牛奶,帐篷, 儿子收到后去准备渔网,滑板等


模式 1:

工作者具体做什么活,根据收到的指令,而指令不是有人当面交代的,而是有个代办指令列表,工作者循环从代办表中,取指令,工作 。。。--任务的发布者不再与执行者直接交互,指令的执行可以是多个工作者,任务发布者也可以是多个。

模式 2

事件驱动:一个事件可以有多个关注着,收到事件后分别去做自己的事。


kafka: 如果一个消费组,就是模式 1,如果多个消费组就是模式 2


编程模型

消息驱动

典型 UI 事件处理模式。注册关注 UI 事件的处理函数(可关注多种事件),没有事件时线程是休息的,有的时候才唤起执行。

可有一个线程去专门负责事件列表,消息列表,发现有就启动任务(可用它线程)执行。一个处理者接受多种个生产者指令。

事件驱动

一个事件可有多个事件监听者,生产者发布自己的事件,有多种消费者关注,在收到后分别去做不同的事。

实现了业务解耦。将来可有扩展很多下层的业务种类(前段业务不关注后端业务)。

解耦了任务分配方与任务执行方,一个事件(消息)可能有多个人都关注,都领取并进行不同的处理


7: 反应式编程

生活场景

分工协作效率高


孩子们都能帮忙了,媳妇把任务分配好,切葱,切土豆,切肉,摘青菜,洗菜,孩子们去抢着做,爸爸负责炖,炒。

切葱土豆肉,洗菜,摘菜是孩子们并行做的,炖肉,炒菜你要等他们有材料齐备的就去做,做完看是否还有齐备的,有就继续做下一道菜。

编程模型

反应式编程,这是前面方法的综合运用。

反应式编程解决的问题


场景:

1>任务拆分为有些可并行,有些需要依赖的子任务。可并行的用多个线程并行执行,缩短任务总体耗时。(异步编程)

2>依赖其他结果的子任务怎么确认可以执行,不要去等待前面的所有线程,而是看自己的消息列表(任务列表)有就去处理。不用一个线程专门等待前面某个任务可以执行,而是多种任务都放到任务列表,去列表中取,或专门负值守线程发现任务后生成任务交给线程池(任务队列,消息驱动,事件驱动-),可减少任务线程的等待,同时也可避免瞬间压力过大开启很多任务线程。


反应式编程的核心:异步编程+非阻塞消息流。

任务要拆分为多个并行--异步编程

拆分提高任务线程的执行效率,就要不阻塞等待

不要进行 IO 耗时的等待-异步 IO

任务间完成通知-异步消息


最后结果:提高执行效率,缩短耗时,并防止系统被压垮。

1>对单个任务由于部分子任务并行执行,提高了响应(耗时短),

2>总体上很多类似的任务,每个任务都拆分到任务列表,子任务间交接用消息队列(相当于转化为任务列表),所有子任务最终都由线程池去任务列表去取,会减少等待的线程数,由于是任务列表,程序也不会压垮。


缺点:

一个请求切换了多次线程,调试不方便

如果 cpu 压力不大,并行部分执行后,用 feature 或 channel 就好。

 

8: 分布式系统下的应用

同一个进程内函数调用,通知等待,任务列表等语言本身都包括了,而在分布式下

1>程序内的调用,改用系统间的调用

2>任务可以由多个线程执行变为可以由多个节点执行

3>本地的任务队列,事件列表改用网络上的消息队列

上述如果要配合好,推荐用框架统一实现,减少编码的复杂度


参考

https://www.cnblogs.com/lguow/p/10750296.html

https://www.cnblogs.com/feiquan/p/11286993.html

https://studygolang.com/articles/20278

https://www.jianshu.com/p/c95e29854cb1


发布于: 2020 年 06 月 21 日阅读数: 169
用户头像

superman

关注

还未添加个人签名 2018.07.20 加入

还未添加个人简介

评论

发布
暂无评论
反应式编程与土豆炖肉