实战项⽬:设计实现⼀个流程编排框架(分析)
最近⼏篇⽂章,我会带⼤家⼀起设计⼀个流程编排框架,从项⽬的分析、设计、实现、重构、测试⽅⾯去了解整个编排框架,也会⽤到⼀些设计开发原则及设计模式,话不多说,我们先来看下编排框架的⼀个背景。
背景
对于交易这样复杂的业务场景,随着时间增加、功能逐渐增多,代码越来越多,所以系统就会考虑使⽤微服务框架;但是使⽤微服务框架之后,原有的业务并没有发⽣变化,与传统架构相⽐,微服务架构下会更依赖通过各微服务之间的协调来实现业务流程,这种协作就是流程编排。编排设计到⽅法节点、服务调⽤、条件选择、串⾏、并⾏、⼦流程等;当然在这个过程中也需要考虑通讯层、分布式事务;需要有⼀个完善的编排框架来⽀撑。
⼀般常见2种编排⽅式
第⼀种:⾯向可执⾏的流程,通过⼀个可执⾏的流程来协同内部及外部的服务交互,通过流程来控制总体的⽬标、涉及的操作、服务调⽤顺序。⾸先要有⼀个流程控制服务,该服务接收请求,依照业务逻辑规则,依次调⽤各个微服务,并最终完成处理逻辑。流程控制服务时时刻刻都知道每⼀笔业务究竟进⾏到了什么地步,监控业务成了相对简单的事情。
第⼆种:API⽹关,API⽹关可以看作⼀种简单的接⼝聚合/拆分的⽅式:每笔业务到来后先到达⽹关,⽹关调⽤各微服务,并最终聚合/拆分需反馈的结果。如果有开放接⼝⽂档还可以⽤编排定制产品,直接达到商⽤的效果。
项⽬背景
我们常规的项⽬是怎么做这种编排的呢?我们列举⼀个例⼦,A->B->C:这种场景是⽐较简单,对于开发⼈员来说就是新建⼀个类先执⾏A 逻辑同步等待执⾏,在执⾏B逻辑同步等待结果在执⾏C;然后对所有结果进⾏处理返回;但是这个逻辑不可能是不变的,可能第⼆天⼜说在B->C加⼀个D逻辑,有需要在之前的业务代码⾥⾯做业务流程新增逻辑,⽽且需要负责的测试;当然后⾯可能还有更多的需求提供来,⽐如其它逻辑要应⽤这个编排,或者在执⾏B时需要加条件执⾏C和D,对于开发⼈员来说是迟早奔溃的事情。
在这个例⼦中我们可以看到⼏个问题:
流程扩展性不强,需要对流程做变更的时候需要在原有逻辑修改代码,对于流程的风险也是⽐较⼤的;
流程可读性不⾏,简单流程逻辑我们还可以理解,但是节点多了之后,就是产⽣各种逻辑、各种设计模式,不管对于⽼员⼯或者新员⼯来说,都是⼀笔不⼩的负担;
代码冗余,在项⽬⾥⾯有过多的我是看到的只是⼀堆代码类似,但是功能实现不⼀样的逻辑;某⼀段逻辑出现问题,需要改涉及到的所有流程,还有可能改漏的风险性;
缺少开发规范,⼀万个⼈就有⼀万个哈姆雷特,只要能实现功能,只要能完成任务就⾏,根本考虑不到谁来接⼿的问题;
缺少监控⼿段,不知道这个流程定义了⼏个节点,每个节点耗时,是那个节点出现的问题,对于运维来说也是⼤问题;
针对上⾯的这些问题,我们有什么建议呢?我先来说说的我的;
我们可以把被调⽤的服务或者是简单封装成⼀个个的基础组件,规范输⼊输出,把调⽤条件封装成⼀个个要素;通过⽂件或者是外部接⼝做服务编排;⽐如我定义⼀个l⽂件对我的基础组件就⾏编排,完全不⽤再代码⾥⾯去做业务逻辑实现。
我们希望我们开发出来的东西有⼀定的影响能⼒,即使做不到在⾏业⾥⾯有影响⼒,起码也要做到在公司范围内有影响⼒。所以,从⼀开始,我们就不想把这个编排框架,做成只有我们项⽬可以,⽽是⼀个通⽤框架,可以集成到各个系统,甚⾄集成到微服务治理平台中。实际上,这也体现了业务开发中要具备的抽象意识、框架意识。我们要善于识别出通⽤的功能模块,将它抽象成通⽤的框架、组件、类库等。
需求分析
刚刚我们花了⼀些篇幅项⽬背景和背景需求,接下来,我们再对需求进⾏更加详细的分析和整理。
功能性需求
⾸先我们需要定义编排规则,但是没有可视化操作的界⾯的前提下,我们会把编排定义在yml、properties、xml中,我们先看⼀个demo ⽰例:
name: openAccount
id: test
desc: 条件执⾏
input: com.service.del.TestInput
output: com.service.del.TestOutput
temp: com.service.del.TestTempspring系列框架有哪些
startNode: node1
nodes:
- node:
id: node1
name: methodInvoke
component: com.service.flow.samplemonponent.TestComponent:test1
desc: 单节点执⾏
input: com.service.del.TestInput
type: method
next: node2
- node:
id: node2
name: beanInvoke
component: testComponent:test2
desc: 单节点执⾏
input: com.service.del.TestInput
type: bean
next: node3
- node:
id: node3
name: conditionByAge
component: Age>20:Age<20:node5
desc: 单节点执⾏
input: com.service.del.TestInput
type: condition
- node:
id: node4
name: beanInvoke
component: testComponent:test2
desc: 单节点执⾏
input: com.service.del.TestInput
type: bean
- node:
id: node5
name: beanInvoke
component: testComponent:test2
desc: 单节点执⾏
input: com.service.del.TestInput
type: bean
我们需要定义⼀些元素请求参数、输出参数、执⾏ID、流程描述、临时变量、节点类型,执⾏条件;
我们需要提供多种编排注册⽅式yml、json、xml、properties、text、接⼝;
我们采⽤链表的形式执⾏所有的节点,减少内存开销;
需要⽀持多种类型节点,⽅法节点、服务节点、条件节点、Bean节点、⼦流程节点等;
我们需要对流程上下⽂做扩展;
我们框架需要基于Spring做基层框架,并提供对应的starter,做可集成框架;
⾮功能性需求
易⽤性⽅⾯,我们希望提供流程提⽰,有能⼒的情况下提供可视化编排,我们将以Spring框架作为基础框架,我们还希望编排框架能否⾮常⽅便地集成到使⽤ Spring 框架的项⽬中。
扩展性、灵活性⽅⾯,我们希望能够⾃⼰定义各种组件和元素模型,我们希望编排初始化的时候提供各种扩展接⼝,将不同类型的⽂本,转换成编排模型;
性能⽅⾯:我们希望模型在项⽬启动的时候就初始化好,只初始化⼀次;节点执⾏的时候采⽤递归链表的形式,减少不必要的代码循环;
容错性⽅⾯,不能因为编排框架⽽影响的本⾝的业务逻辑,即使不⽤这个框架也不⾄于框架⼤乱。
功能需求其实没有多少,但将⾮功能性需求考虑进去之后,明显就复杂了很多。还是那句⽼话,写出能⽤的代码很简单,写出好⽤的代码很难。对于编排框架来说,⾮功能性需求是设计与实现的难点。怎么做到易⽤、灵活、可扩展、低延迟、⾼容错,才是开发的重点。
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论