Liteflow服务编排简介

755 阅读6分钟

1. 简介

LiteFlow是一个非常强大的现代化的规则引擎框架,融合了编排特性和规则引擎的所有特性,能将复杂的业务流程通过组件化的方式解耦每个步骤、重新编排以打造一个低耦合又灵活的系统。

重点概念: 规则组件

利用LiteFlow,你可以将瀑布流式的代码,转变成以组件为核心概念的代码结构,这种结构的好处是可以任意编排,组件与组件之间是解耦的,组件可以用脚本来定义,组件之间的流转全靠规则来驱动。

编排规则: 组件按照一定逻辑顺序执行的约束,也可称之为 业务逻辑

复杂的编排示例:

2. 组件

组件是抽取业务流程的原子性操作的代码实现, 通常单个组件是一个类,并且根据不同类型继承LiteFlow的基础组件, 并实现基类的process方法。

组件分为:普通组件、选择组件、条件组件、循环组件

涉及规则关键字: THENWHENSWITCHIF...ELIF...ELSE、FOR...DO...、WHILE...DO...、ITERATOR...DO...****

This关键字 :this关键字在组件中用于获取上下文、使用 this.getContextBean(clazz) 获取整个业务流程中的所有需要的数据,更详细用法会放在文章末尾。

2.1 普通组件

普通节点需要继承基类 NodeComponent , 可用于 THEN 和 WHEN 关键字中,并实现基类的 process方法

@LiteflowComponent("a")
    public class ACmp extends NodeComponent {

        @Override
        public void process() {
    	// 业务代码
            
        }
    }

注: LiteflowComponent 注解中的 参数a , 就是规则中的组件id

2.2 选择组件

依据业务动态选择下级节点的组件、需要继承基类 NodeSwitchComponent ,可用于 SWITCH关键字中,并实现基类的 processSwitch 方法

@LiteflowComponent("a")
public class ACmp extends NodeSwitchComponent {

    @Override
    public String processSwitch() throws Exception {
        // 业务代码
        return "c";
    }
}

注: LiteflowComponent 注解中的 参数 a,就是规则中的组件id, 最后return 的 c 就是下一节点的组件id

2.3 条件组件

依据业务返回true/false的组件、也成为IF组件, 需要继承 NodeIfComponent,可用于 IF...ELIF...ELSE 等关键字

@Component("x")
public class XCmp extends NodeIfComponent {
	@Override
	public boolean processIf() throws Exception {
	    //业务代码
		return true;
	}
}

注: Component 注解和LiteflowComponent 注解等效,只是后者增加许多方法,上面的x是规则中的组件id, 最后return 的 true会给规则、用于三元表达式中选择下一节点

2.4 循环组件

依据业务针对部分需要循环执行的组件、比如分页查询某个接口的场景、也分为次数循环、条件循环、迭代循环等组件、分别需要继承基类 。

次数循环需要继承 NodeForComponent 基类,并实现processFor方法,可用于 FOR...DO... 关键字

条件循环需要继承 NodeWhileComponent 基类, 并实现 processWhile 方法、可用于 WHILE...DO...关键字

迭代循环需要继承 NodeIteratorComponent 基类,并实现 processIterator 方法, 可用于 ITERATOR...DO... 关键字

退出循环需要继承 NodeBreakComponent 基类, 并实现 processBreak 方法, 可用于 BREAK 关键字

/*** 次数循环组件 ***/
@LiteflowComponent("f")
public class FCmp extends NodeForComponent {
    @Override
    public int processFor() throws Exception {
        //这里根据业务去返回for的结果
    }
}

/*** 条件循环组件 ***/
@LiteflowComponent("w")
public class WCmp extends NodeWhileComponent {
    @Override
    public boolean processWhile() throws Exception {
        //这里根据业务去返回while的结果
    }
}

/*** 迭代循环组件 ***/
@LiteflowComponent("x")
public class XCmp extends NodeIteratorComponent {
    @Override
    public Iterator<?> processIterator() throws Exception {
        List<String> list = ListUtil.toList("jack", "mary", "tom");
        return list.iterator();
    }
}

/*** 退出循环组件 ***/
@LiteflowComponent("c")
public class CCmp extends NodeBreakComponent {
    @Override
    public boolean processBreak() throws Exception {
        //这里根据业务去返回break的结果
    }
}

3. 规则

使用EL表达式描述组件之间业务流程的内容

规则分类: 串行编排、并行编排、条件编排、循环编排等

涉及关键字:THEN、WHEN、SWITCH、IF、 F OR...DO...、WHILE...DO...、ITERATOR...DO...****

3.1 串行编排

规则内部的组件执行逻辑属于串行操作,使用关键字 THEN 引导

<chain name="chain1">
  THEN(a, b, c, d);
</chain>

注: 该表达式表示的是 从组件 a 然后是b、接着c、d依次执行

3.2 并行编排

规则内部的组件执行逻辑属于并行操作、类似多线程并行,使用关键字 WHEN引导

<chain name="chain1">
    THEN(
        a,
        WHEN(b, c, d),
        e
    );
</chain>

注: 该表达式表示 组件 b、c 、d同时执行、不分先后顺序

3.3 选择编排

规则内部是选择逻辑、根据业务返回选择的结果,使用 SWITCH引导

<chain name="chain1">
    SWITCH(a).to(b, c, d);
</chain>

注:该表达式表示依据组件a,返回的结果判断下一节点走b、c、d哪个组件

3.4 循环编排

循环分为 次数循环、条件循环、编排循环,使用F OR...DO...、WHILE...DO...、ITERATOR...DO... ****关键字引导

/*** 次数循环 ***/
<chain name="chain1">
    FOR(f).DO(THEN(a, b));
</chain>

注: 组件f返回需要循环执行的次数、然后a->b这个链路依据组件f返回的次数循环

/*** 条件循环 ***/
<chain name="chain1">
    WHILE(w).DO(THEN(a, b));
</chain>

注:组件w会返回true/false,如果是true则继续下一节点a->b,否则不执行下一节点

/*** 迭代循环 ***/
<chain name="chain1">
    ITERATOR(x).DO(THEN(a, b));
</chain>

注: 组件x会返回一个迭代器如 list.iterator() 然后a->b的链路执行

4. 规则文件

用于存储组件及规则内容EL表达式的文件、可以存在于数据库动态化存储、也可以以文件的形式存在项目中

<?xml version="1.0" encoding="UTF-8"?>
<flow>
    <nodes>
        <node id="a" class="com.yomahub.liteflow.test.parser.cmp.ACmp"/>
        <node id="b" class="com.yomahub.liteflow.test.parser.cmp.BCmp"/>
        <node id="c" class="com.yomahub.liteflow.test.parser.cmp.CCmp"/>
        <node id="d" class="com.yomahub.liteflow.test.parser.cmp.DCmp"/>
    </nodes>

    <chain name="chain1">
        THEN(
            a, b, WHEN(c,d)
        );
    </chain>
</flow>

注:node节点是组件信息、id标记的就是组件id、class是组件的类路径

chain 标记的就是编排EL表达式, name是编排规则的名称

存储项目中的位置

5. 执行器与数据上下文

执行器是一个流程的触发点、用于开始一个编排流程,可以在任意的类中注入 FlowExecutor ,然后调用其方法即可。

数据上下文是在一个流程中的组件之间是解耦的、数据上下文用于存储流程中的数据,使用This关键字可以在各组件内部获取所有该流程内流转的数据信息。

/*** 列举了 FlowExecutor 基类中定义的两个常用的执行器方法 ***/

//第一个参数为流程ID,第二个参数为流程入参,后面可以传入多个上下文class
public LiteflowResponse execute2Resp(String chainId, Object param, Class<?>... contextBeanClazzArray)
//第一个参数为流程ID,第二个参数为流程入参,后面可以传入多个上下文的Bean
public LiteflowResponse execute2Resp(String chainId, Object param, Object... contextBeanArray)


/*** 实际使用的模板 ***/
public class LiteFlowService {

    @Resource
    private FlowExecutor customerFlowExecutor;

    /**
     * 调用执行器
     *
     * @param executeParam 入参
     */
    public String executeFlow(ExecuteParam executeParam){
        String chainId = executeParam.getChainId();
        
        log.info("入参:executeParam=[{}]", JsonUtil.toJsonString(executeParam));
        LiteflowResponse chainResponse = customerFlowExecutor.execute2Resp(chainId, executeParam.getParam(), User.class, Grade.class, Order.class, OrderSubDTO.class, Map.class);
       
        //log.info("回参:chainResponse=[{}]", JsonUtil.toJsonString(chainResponse));
        if (!chainResponse.isSuccess()){
            Exception cause = chainResponse.getCause();
            log.info("异常信息cause=[{}]", JsonUtil.toJsonString(cause));
            chainResponse.getCode();
        }


        Map<String, CmpStep> executeSteps = chainResponse.getExecuteSteps();
        log.info("执行步骤:executeSteps=[{}]", JsonUtil.toJsonString(executeSteps));

        Object responseData = chainResponse.getSlot().getResponseData();
        log.info("返回前端: responseData=[{}]", JsonUtil.toJsonString(responseData));

        Grade grade = chainResponse.getSlot().getContextBean(Grade.class);
        User user = chainResponse.getSlot().getContextBean(User.class);
        Order order = chainResponse.getSlot().getContextBean(Order.class);
        log.info("最后获得grade=[{}] user=[{}], order=[{}]", CoreJsonUtil.objectToJson(grade), CoreJsonUtil.objectToJson(user), CoreJsonUtil.objectToJson(order));

        return JsonUtil.toJsonString(responseData);
    }

}

注:列举两个常用的执行器、这是 FlowExecutor 基类中的实现方法,我们使用时候需要注入该基类、调用方法即可

@LiteflowComponent("ageHigh")
public class AgeHighComponent extends NodeComponent {


    @Override
    public void process() throws Exception {
        log.info("当前用户大于10岁,属于高年龄!");

        // 获取到组件参数对象
        User user = this.getSlot().getContextBean(User.class);

        // 组装组件参数体
        user = CoreReflectUtil.getReflectedClassData(user, this.getRequestData());

        // 执行业务操作
        if(ObjectUtil.isNotNull(user)){
            user.setExecuteStep(user.getExecuteStep() + "|=====>>>>|" + "执行AgeHighComponent组件");
        }

        // 组件返回参数
        this.getSlot().setResponseData(user);
    }
}

注:组件内使用this关键字获取组件业务流程之间流转的数据