flowable工作流
Survive by day and develop by night.
talk for import biz , show your perfect code,full busy,skip hardness,make a better result,wait for change,challenge Survive.
happy for hardess to solve denpendies.
目录
概述
工作流,是把业务之间的各个步骤以及规则进行抽象和概括性的描述。使用特定的语言为业务流程建模,让其运行在计算机上,并让计算机进行计算和推动。
需求:
工作流是复杂版本的状态机。
上图为工作流退化为基础状态机的例子,小明的状态非常简单,站立->走路->跑步->走路->站立,无限循环,如果让我们实现小明的状态切换,那么我们只需要用一个字段来记录小明当前的状态就好了。
设计思路
实现思路分析
1.复杂的状态的或者状态的维度增加的状的条件极为复杂
而对于复杂的状态或者状态维度增加且状态流转的条件极为复杂,可能单纯用字段记录状态的实现方式就会不那么理想。
2.工作流
工作流解决的痛点在于,解除业务宏观流程和微观逻辑的耦合,让熟悉宏观业务流程的人去制定整套流转逻辑,而让专业的人只需要关心他们应当关心的流程节点,就好比大家要一起修建一座超级体育场,路人甲只需要关心他身边的这一堆砖是怎么堆砌而非整座建筑。
3.BPMN2.0协议
对于业务建模,我们需要一种通用的语言来描绘,这样在沟通上和实现上会降低难度,就像中文、英文一样,BPMN2.0便是一种国际通用的建模语言,他能让自然人轻松阅读,更能被计算机所解析。
4.协议的元素
协议中元素的主要分类为,事件-任务-连线-网关。
一个流程必须包含一个事件(如:开始事件)和至少一个结束(事件)。其中网关的作用是流程流转逻辑的控制。任务则分很多类型,他们各司其职,所有节点均由连线联系起来。
5.互斥网关
又称排他网关,他有且仅有一个有效出口,可以理解为if…else if… else if…else,就和我们平时写代码的一样。
包容性网关(Inclusive Gateway)
包容性网关(Inclusive Gateway),只要满足条件的出口都会执行,可以理解为 if(…) do, if (…) do, if (…) do,所有的条件判断都是同级别的。
BPMN2.0协议的所有任务其实是从一个抽象任务派生而来的,抽象任务会有如下行为:
当流程流转到该任务时,应该做些什么?
当该任务获得信号(signal)的时候,它是否可以继续向下流转,而任务获得信号的这个动作我们称为Trigger。
利用如上的抽象行为,我们来解释一些比较常见且具有代表性的任务类型。
人工任务(User Task)它是使用得做多的一种任务类型,他自带有一些人工任务的变量
例如签收人(Assignee),签收人就代表该任务交由谁处理,我们也可以通过某个特定或一系列特定的签收人来查找待办任务。
利用上面的行为解释便是,当到达User Task节点的时候,节点设置Assignee变量或等待设置Assignee变量,当任务被完成的时候,我们使用Trigger来要求流程引擎退出该任务,继续流转。
服务任务(Service Task),该任务会在到达的时候执行一段自动的逻辑并自动流转。从“到达自动执行一段逻辑”这里我们就可以发现,服务任务的想象空间就可以非常大,我们可以执行一段计算,执行发送邮件,执行RPC调用,而使用最广泛的则为HTTP调用,因为HTTP是使用最广泛的协议之一,它可以解决大部分第三方调用问题,在我们的使用中,HTTP服务任务也被我们单独剥离出来作为一个特殊任务节点。
接受任务(Receive Task),该任务的名字让人费解,但它又是最简单的一种任务,当该任务到达的时候,它不做任何逻辑,而是被动地等待Trigger,它的适用场景往往是一些不明确的阻塞,比如:一个复杂的计算需要等待很多条件,这些条件是需要人为来判断是否可以执行,而不是直接执行,这个时候,工作人员如果判断可以继续了,那么就Trigger一下使其流转。
调用活动(Call Activity),调用活动可以理解为函数调用,它会引用另外一个流程使之作为子流程运行,调用活动跟函数调用的功能一样,使流程模块化,增加复用的可能性。
这里是一个生产汽车的流程,从“汽车设计”节点到“批准生产”节点是一个串行的任务,而审批的结果会遇到一个互斥网关,上面讲过,互斥网关只需要满足其中一个条件就会流转,而这里表达的意义就是审批是否通过。“载入图纸”是一个服务任务,它是自动执行的,之后会卡在“等待原材料”这个节点,因为这个节点是需要人为去判断(比如原材料涨价,原材料不足等因素),所以需要在一种自定义的条件下Trigger,而该图的条件应该为“原材料足够”,原材料足够之后,我们会开始并行生产汽车零件。
需要注意的是,并行网关在图中是成对出现的,他的作用是开始一系列并行任务和等待并行任务一起完成,拿一个Java中的东西举例子,就是CountDownLatch,fork-join模型也可以类比。
网关的本质其实是计数器和出口逻辑的混合,它跟其他节点没什么区别,只是他的推动逻辑需要使他的计数器为0,而计数器的总数为网关入口线段的数量,比如“组装”节点前面的并行网关,他的计数器就为4,而前面4个节点,每完成一个就会触发该网关计数器-1。
当计数器为0的时候,网关会触发选择后续流转的逻辑。
互斥网关的逻辑为:遍历所有出口连线,满足条件则流出并打断(也就是break掉)。
并行网关的逻辑为:遍历所有出口连线,无条件为所有连线流出。
包容性网关的逻辑为:遍历所有出口连线,满足条件的就流出。
Flowable是BPMN2.0协议的一种Java版本的实现。
Flowable项目提供了一组核心的开源业务流程引擎,这些引擎紧凑且高效。它们为开发人员、系统管理员和业务用户提供了一个工作流和业务流程管理(BPM)平台。它的核心是一个非常快速且经过测试的动态BPMN流程引擎。它基于Apache2.0开源协议,有稳定且经过认证的社区。
Flowable可以嵌入Java应用程序中运行,也可以作为服务器、集群运行,更可以提供云服务。
目前主流的工作流开源框架就是Activiti/Camunda/Flowable,它们都有一个共同的祖先jbpm。先是有了jbpm4,随后出来了一个Activiti5,Activiti5经过一段时间的发展,核心人员出现分歧,又分出来了一个Camunda。activiti5发展了4年左右,紧接着就出现了Flowable。
而Activiti则只着重于处理bpmn,它的方向在于云,他的设计会尽量像例如Spring Cloud、Docker、K8S靠拢。
如果你单纯地想快速上手bpmn引擎,建议使用Activiti,如果你想做出花样繁多的工作流引擎,建议使用Flowable。
而Camunda(卡蒙达)则更加的轻巧灵活,他的初衷就是为开发人员设计的“小工具”,但我个人的感觉而言,Camunda从代码上看并没有Activiti和Flowable好,而且他的社区是最不活跃的一个(至少从国内的角度来看),所以不太建议使用(当然这带了很多个人主观感受,如有不同意见,欢迎讨论)。
从架构图中可以看出,Flowable对于数据的处理是冷热分离的,热数据存在ACT_RU_系列表中,历史冷数据存在ACT_HI_系列表中,定义相关的存在ACT_RE_系列表中,而对于在途和定义相关的数据,有一层缓存,他缓存的具体实现比较复杂。
Flowable开发了新的异步执行器(ASYNC EXECUTOR)
,异步执行大概分为两类,timer和message,类似于定时事件就是timer,而异步的服务任务则为message。
“Task A”附着的边界定时事件,在时间触发之后,会执行“Escalate”任务,而“Async Service Task”在“Task A”流转之后,会启用一个异步任务去调用其服务。
对于一种全是异步服务任务的极端情况,如上图所示,他常常出现于服务编排之类的场景之中,我们经常性的需要同时处理一系列的任务,这时候异步调度的作用就非常重要。
Flowable也采用了冷热数据分离的思想,他把数据分为了4类,异步Job,定时器Timer,挂起任务,死信队列。通过测试发现,数据库是异步任务性能低下的主要瓶颈,特别是多实例竞争Job会存在潜在的问题。在分表的时候同时加上了一个全局锁,保证了同一个实例只能由一个实例去获取并锁定job(job表中有字段会被update,内容为抢占到的实例代号),这样反而能提升不少性能。为了保证各个实例不被饿死,还调整了一系列参数。
Flowable提供了一个更加优化的冷热数据分离方案,在数据敏感性比较高的领域,我们一般会把引擎的历史记录级别调到最高(包括流程流转历史、变量变动历史、签收人变动历史等等),这些历史记录在以前是在同一个上下文中执行的,虽然在最开始设计的时候,在途数据和历史数据就冷热分离了,但从权衡在途和历史的重要性的角度来讲,历史其实不是最重要的,所以Flowable提供一系列的方法使历史记录这个行为异步化,与之对应的方法可以是jms,rabbitMQ或Spring Boot listener application。
这个改动可以提升在途流程效率20%-96%。
云上的工作流应该关心什么
我如何把我的业务逻辑转化为流程图-即容易理解的绘图工具。
我如何使流程流转-即开箱即用的API。
我需要引擎告诉我,我现在该处理什么节点-即丰富且鲜明的事件机制。
图中是流程图的整个生命周期,从画图到部署,然后启动流程,流程经过人工或自动的方式流转,最后结束。
还有一些费解的属性,比如,排他、同步异步、相对的,我们只保留一些常见的节点类型,就如前面介绍的:用户任务、服务任务、互斥网关、并行网关等。
而开箱即用的API就需要我们尽量减少API的复杂度和个数,把API分类为以下三类比较合适。
流程定义类-负责流程定义的查看
流程实例类-负责流程实例的查看与操作
而对于“我现在该处理什么节点”的问题,我们提供了一种解决方案。
用户的角色可以分为三类逻辑,第一、和工作流沟通的逻辑,它负责启动流程和通知流程的流转,
第二类为服务提供者,即工作流不能提供的服务,需要第三方或业务方自己计算结果,比如:支付接口。
第三类为消息处理逻辑,这里的消息大概为任务的创建,任务的签收,任务的完成,流程的创建,流程的结束等等,处理消息的角色可以根据自己的职责处理不同的任务类型或流程类型。
用户的角色可以分为三类逻辑,第一、和工作流沟通的逻辑,它负责启动流程和通知流程的流转,第二类为服务提供者,即工作流不能提供的服务,需要第三方或业务方自己计算结果,比如:支付接口。第三类为消息处理逻辑,这里的消息大概为任务的创建,任务的签收,任务的完成,流程的创建,流程的结束等等,处理消息的角色可以根据自己的职责处理不同的任务类型或流程类型。
这样区分的好处在于,如果有一个流程图,他的流程涉及到不同系统甚至是不同部门之间的合作,我们不可能让所有部门都去关心整个流程,甚至有些部门根本不知道工作流的存在,他们所关心的。
参考资料和推荐阅读
- https://zhuanlan.zhihu.com/p/417014073
欢迎阅读,各位老铁,如果对你有帮助,点个赞加个关注呗!~