1. 前言
在每个公司的系统中,总有一些拥有复杂业务逻辑的系统,这些系统承载着核心业务逻辑,几乎每个需求都和这些核心业务有关,这些核心业务业务逻辑冗长,涉及内部逻辑运算,缓存操作,持久化操作,外部资源调取,内部其他系统RPC调用等等。时间一长,项目几经易手,维护成本就会越来越高。各种硬代码判断,分支条件越来越多。代码的抽象,复用率也越来越低,各个模块之间的耦合度很高。一小段逻辑的变动,会影响到其他模块,需要进行完整回归测试来验证。如要灵活改变业务流程的顺序,则要进行代码大改动进行抽象,重新写方法。实时热变更业务流程,几乎很难实现
如何打破僵局?LiteFlow为解耦逻辑而生,为编排而生,在使用LiteFlow之后,你会发现打造一个低耦合,灵活的系统会变得易如反掌!
LiteFlow是一个非常强大的现代化的规则引擎框架,融合了编排特性和规则引擎的所有特性
2. LiteFlow框架的优势
如果你要对复杂业务逻辑进行新写或者重构,用LiteFlow最合适不过。它是一个编排式的规则引擎框架,组件编排,帮助解耦业务代码,让每一个业务片段都是一个组件
利用LiteFlow,你可以将瀑布流式的代码,转变成以组件为核心概念的代码结构,这种结构的好处是可以任意编排,组件与组件之间是解耦的,组件可以用脚本来定义,组件之间的流转全靠规则来驱动。LiteFlow拥有开源规则引擎最为简单的DSL语法。十分钟就可上手
组件可实时热更替,也可以给编排好的逻辑流里实时增加一个组件,从而改变你的业务逻辑。

LiteFlow支持把编排规则和脚本放在数据库,注册中心中,还有可以任意扩展的接口,方便你定制。

2.1 LiteFlow的设计原则
LiteFlow是基于工作台模式进行设计的,何谓工作台模式?
n个工人按照一定顺序围着一张工作台,按顺序各自生产零件,生产的零件最终能组装成一个机器,每个工人只需要完成自己手中零件的生产,而无需知道其他工人生产的内容。每一个工人生产所需要的资源都从工作台上拿取,如果工作台上有生产所必须的资源,则就进行生产,若是没有,就等到有这个资源。每个工人所做好的零件,也都放在工作台上
这个模式有几个好处:
- 每个工人无需和其他工人进行沟通。工人只需要关心自己的工作内容和工作台上的资源。这样就做到了每个工人之间的解耦和无差异性。
- 即便是工人之间调换位置,工人的工作内容和关心的资源没有任何变化。这样就保证了每个工人的稳定性。
- 如果是指派某个工人去其他的工作台,工人的工作内容和需要的资源依旧没有任何变化,这样就做到了工人的可复用性。
- 因为每个工人不需要和其他工人沟通,所以可以在生产任务进行时进行实时工位更改:替换,插入,撤掉一些工人,这样生产任务也能实时的被更改。这样就保证了整个生产任务的灵活性。
个模式映射到LiteFlow框架里,工人就是组件,工人坐的顺序就是流程配置,工作台就是上下文,资源就是参数,最终组装的这个机器就是这个业务。正因为有这些特性,所以LiteFlow能做到统一解耦的组件和灵活的装配
2.2 LiteFlow使用场景
LiteFlow适用于拥有复杂逻辑的业务,比如说价格引擎,下单流程等,这些业务往往都拥有很多步骤,这些步骤完全可以按照业务粒度拆分成一个个独立的组件,进行装配复用变更。使用LiteFlow,你会得到一个灵活度高,扩展性很强的系统。因为组件之间相互独立,也可以避免改一处而动全身的这样的风险
3. 代码工程
liteFlow可以帮助我们快速编排复杂的业务规则,并实现动态的规则更新。
3.1 普通组件版
- 引入依赖 - 1 
 2
 3
 4
 5- <dependency> 
 <groupId>com.yomahub</groupId>
 <artifactId>liteflow-spring-boot-starter</artifactId>
 <version>2.12.2.3</version>
 </dependency>
- 组件定义 - 组件a - 1 
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13- import com.yomahub.liteflow.annotation.LiteflowComponent; 
 import com.yomahub.liteflow.core.NodeComponent;
 /**
 * @author xiaoyuge
 */
 public class AComponent extends NodeComponent {
 
 public void process() {
 System.out.println("-----a-------");
 }
 }- 组件b - 1 
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13- import com.yomahub.liteflow.annotation.LiteflowComponent; 
 import com.yomahub.liteflow.core.NodeComponent;
 /**
 * @author xiaoyuge
 */
 public class BComponent extends NodeComponent {
 
 public void process() {
 System.out.println("-----b-------");
 }
 }- 组件c - 1 
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13- import com.yomahub.liteflow.annotation.LiteflowComponent; 
 import com.yomahub.liteflow.core.NodeComponent;
 /**
 * @author xiaoyuge
 */
 public class CComponent extends NodeComponent {
 
 public void process() {
 System.out.println("-----c-------");
 }
 }
- application.yml 配置文件 - 1 
 2- liteflow: 
 rule-source: classpath:flow.xml- 也可以自定配置类使用注解 - @LiteFLowConfig来指定配置文件- 1 
 2
 3
 4
 public class YourLiteflowConfig {
 // ...
 }
- 规则文件定义 - 1 
 2
 3
 4
 5
 6
 <flow>
 <chain name="chain1">
 THEN(a, b, c);
 </chain>
 </flow>
- 启动类 - 1 
 2
 3
 4
 5
 6
 7
 public class LiteFlowDemoApplication {
 public static void main(String[] args) {
 SpringApplication.run(LiteFlowDemoApplication.class, args);
 }
 }
- 测试类 - 1 
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23- package org.example; 
 import com.yomahub.liteflow.core.FlowExecutor;
 import com.yomahub.liteflow.flow.LiteflowResponse;
 import org.springframework.web.bind.annotation.GetMapping;
 import org.springframework.web.bind.annotation.RestController;
 import javax.annotation.Resource;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.Future;
 public class LiteFlowController {
 
 private FlowExecutor flowExecutor;
 
 public void index() throws ExecutionException, InterruptedException {
 final Future<LiteflowResponse> liteflowResponseFuture = flowExecutor.execute2Future("chain1", "arg");;
 System.out.println(liteflowResponseFuture.get());
 }
 }
- 启动并访问测试类,输出日志如下 - 1 
 2
 3
 4
 5- -----a------- 
 -----b-------
 -----c-------
 a<0>==>b<0>==>c<0>
 com.yomahub.liteflow.flow.LiteflowResponse@1fce8e14
3.2 进阶版
- 编写两个组件 - 1 
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 public class MessageNode extends NodeComponent {
 
 public void process() {
 Slot slot = this.getSlot();
 System.out.println("获取参数"+slot.getRequestData().toString());
 System.out.println("-----发送消息逻辑------");
 }
 }
 public class PointNode extends NodeComponent {
 
 public void process() {
 //获取参数
 Slot slot = this.getSlot();
 System.out.println("获取参数"+slot.getRequestData().toString());
 System.out.println("-----发放积分逻辑------");
 }
 }
- 编写规则文件 - 1 
 2
 3
 4
 5
 6
 7
 <flow>
 <chain name="flow">
 <!--在同一个并行组中, then 和 when 来表示同步节点和异步节点。 -->
 THEN(WHEN(messageNode, pointNode));
 </chain>
 </flow>
- 流程触发 - 1 
 2
 3
 4
 5
 public void index() {
 //完成订单的其他逻辑,传递参数
 flowExecutor.execute2Resp("flow", "这是order的参数");
 }
4. 扩展
4.1 LiteFlow脚本组件
LiteFlow框架目前一共支持6种脚本语言:Groovy、Javascript、QLExpress、Python、Lua、Aviator
LiteFlow采用SPI机制进行选择脚本框架来动态编译脚本,官方推荐使用Groovy,因为和Java语法最接近,下面使用Groovy为例。
4.2 LiteFLow组件类别
在LiteFlow中,主要有以下几种组件:
- 普通组件:集成NodeComponent,用于执行具体的业务逻辑 1 
 2
 3
 4<!--关键字THEN,继承NodeComponent,实现process方法--> 
 <chain name="chain1">
 THEN(a, b, c);
 </chain>
- 选择组件:通过业务逻辑选择不同的执行路径 1 
 2
 3
 4<!-- 关键字SWITCH, 继承NodeSwitchComponent,实现processSwitch方法 --> 
 <chain name="chain1">
 SWITCH(a).to(b, c);
 </chain>
- 条件组件:基于条件返回结果,决定下一步的业务流程 - 1 
 2
 3
 4- <!--关键字IF...ELIF...ELSE,继承NodeIfComponent,实现processIf方法,表达式如下--> 
 <chain name="chain1">
 IF(x, a, b);
 </chain>
- 次数循环组件:根据循环次数,执行某些普通组件 - 1 
 2
 3
 4- <!--关键字FOR...DO...,继承NodeForComponent,实现processFor方法,表达式如下:--> 
 <chain name="chain1">
 FOR(f).DO(THEN(a, b));
 </chain>
- 条件循环组件:根据返回结果的true/false,来判断是否要执行某些普通组件 1 
 2
 3
 4<!--关键字WHILE...DO...,继承NodeWhileComponent,实现processWhile方法,表达式如下:--> 
 <chain name="chain1">
 WHILE(w).DO(THEN(a, b));
 </chain>
- 迭代循环组件:根据返回结果的true/false,来判断是否要执行某些普通组件 - 1 
 2
 3
 4- <!--关键字ITERATOR...DO...,继承NodeIteratorComponent,实现processIterator方法,表达式如下:--> 
 <chain name="chain1">
 ITERATOR(x).DO(THEN(a, b));
 </chain>
- 退出循环组件: - 1 
 2
 3
 4
 5
 6
 7
 8- <!--关关键字BREAK,继承NodeBreakComponent,实现processBreak方法,主要用于以下组合:FOR...DO...BREAK,WHILE...DO...BREAK,ITERATOR...DO...BREAK--> 
 <chain name="chain1">
 FOR(f).DO(THEN(a, b)).BREAK(c);
 </chain>
 <chain name="chain1">
 WHILE(w).DO(THEN(a, b)).BREAK(c);
 </chain>
4.3 脚本的定义
脚本组件也是定义在规则文件中的,以xml为例
| 1 | 
 | 
这里定义了一个e节点,name为别名, type是节点的类型,language代表使用的语言。
type的分类:
- script: 普通脚本节点,脚本无需返回
- switch_script: 选择脚本节点,脚本里需要返回选择的节点ID
- if_script: 条件脚本节点,脚本里需要返回true/false;
- for_script: 数量循环节点,脚本里需要返回数值,表示循环次数。
- while_script: 条件玄幻节点,脚本里需要返回true/false,表示什么条件才继续循环
- break_script: 退出循环节点,脚本里需要返回true/false,表示什么时候退出循环
4.4 脚本的使用
脚本的使用和Java创建bean一样,直接通过节点ID就可以,如:
| 1 | <chain name="chain2"> | 
修改启动类,指定执行的Chain即可
| 1 | flowExecutor.execute2Resp("chain2", null, null); | 
 
         
              