liteflow学习一

2023-10-08 09:34:19 浏览数 (1)

liteflow的业务逻辑编排能力是非常强,同时也是非常好用的。支持很多种方式的编排,串行编排、并行编排、选择编排、条件编排、循环编排,同时支持使用子流程、使用子变量等等。本文参考liteflow官网,学习liteflow的执行流程,官网对学习liteflow非常友好。

liteflow的开源地址:https://gitee.com/dromara/liteFlow,很感谢作者铂赛东开源这么好用的业务编排框架。

一、liteflow的使用

首先引入liteflow的spring-boot-starter依赖,然后继承 NodeComponent,重写process方法。在application.yml中配置xml中配置资源规则,以说明当前串行器的执行顺序。

比如当前的执行顺序是a->b->c

代码语言:javascript复制
<?xml version="1.0" encoding="UTF-8"?>
<flow>
    <chain name="chain1">
        THEN(a, b, c);
    </chain>
</flow>

在业务系统的业务逻辑层注入FlowExecutor,chain1在业务逻辑执行对应的业务逻辑,也即这个顺序是a->b->c,也即 NodeComponent中的业务逻辑,Bean对应@Component("a")、@Component("b")、@Component("c")。

代码语言:javascript复制
@Component
public class YourClass{

    @Resource
    private FlowExecutor flowExecutor;

    public void testConfig(){
        LiteflowResponse response = flowExecutor.execute2Resp("chain1", "arg");
    }
}

完成编写后,即可执行,执行的顺序a->b->c串行编排。也即执行后可以看到我们想要的结果。

这样简化了我们组装业务逻辑的方式。

这么方便使用,是不是非常好奇怎么实现的呢?

二、如何实现

首先我们还是从example开始,从最基础的builder开始学习。

在脚本中,串行器以THEN开头,而并行器以WHEN开头。

代码语言:javascript复制
@Test
    public void testBuilder() throws Exception {
        // 基于liteflowNode构建器创建节点
        LiteFlowNodeBuilder.createNode()
            .setId("a")
            .setName("组件A")
            .setType(NodeTypeEnum.COMMON)
            .setClazz("com.yomahub.liteflow.test.builder.cmp1.ACmp")
            .build();
        LiteFlowNodeBuilder.createNode()
            .setId("b")
            .setName("组件B")
            .setType(NodeTypeEnum.COMMON)
            .setClazz("com.yomahub.liteflow.test.builder.cmp1.BCmp")
            .build();
        LiteFlowNodeBuilder.createNode()
            .setId("c")
            .setName("组件C")
            .setType(NodeTypeEnum.COMMON)
            .setClazz("com.yomahub.liteflow.test.builder.cmp1.CCmp")
            .build();
        LiteFlowNodeBuilder.createNode()
            .setId("d")
            .setName("组件D")
            .setType(NodeTypeEnum.COMMON)
            .setClazz("com.yomahub.liteflow.test.builder.cmp1.DCmp")
            .build();
        LiteFlowNodeBuilder.createNode()
            .setId("e")
            .setName("组件E")
            .setType(NodeTypeEnum.SWITCH)
            .setClazz("com.yomahub.liteflow.test.builder.cmp1.ECmp")
            .build();
        LiteFlowNodeBuilder.createNode()
            .setId("f")
            .setName("组件F")
            .setType(NodeTypeEnum.COMMON)
            .setClazz("com.yomahub.liteflow.test.builder.cmp1.FCmp")
            .build();
        LiteFlowNodeBuilder.createNode()
            .setId("g")
            .setName("组件G")
            .setType(NodeTypeEnum.COMMON)
            .setClazz("com.yomahub.liteflow.test.builder.cmp1.GCmp")
            .build();

        // 创建节点后,创建chain链路
        LiteFlowChainELBuilder.createChain().setChainName("chain2").setEL("THEN(c, d)").build();

        // 创建链路
        LiteFlowChainELBuilder.createChain()
            .setChainName("chain1")
            .setEL("THEN(a, b, WHEN(SWITCH(e).to(f, g, chain2)))")
            .build();

        // 基于流程执行器执行链路1
        LiteflowResponse response = flowExecutor.execute2Resp("chain1");
        Assertions.assertTrue(response.isSuccess());
        Assertions.assertEquals("a[组件A]==>b[组件B]==>e[组件E]==>c[组件C]==>d[组件D]", response.getExecuteStepStr());
    }

flowExecutor.execute2Resp("chain1")这个方法是业务逻辑执行的核心方法。但是在此之前会执行一些的方法:

1)LiteFlowNodeBuilder创建a到g组件节点。从代码中可以看到节点信息最终会添加到FlowBus中,使用了FlowBus.addNode(this.node.getId(), this.node.getName(), this.node.getType(), this.node.getClazz())。

2)LiteFlowChainELBuilder.createChain()创建chain的过程build中可以看到最终将数据也添加到了Chain中之外,还将chain添加到了chainMap中。

3)完成后,通过流程执行器完成请求 flowExecutor.execute2Resp("chain1")。

根据debug的信息,可以看到LiteflowExecutorInit实现了InitializingBean,重写afterPropertiesSet方法,此时会对flowExecutor做一次初始化,执行初始化Cmp,对应ruleResource进行判断,如果LiteflowConfig不存在配置信息,则加载SPI中,通过类加载器获取,如果存在,则加载,同时放入到liteflowConfig中,否则进行返回。

也即此时执行单元测试基于 flowExecutor.execute2Resp("chain1")进行执行后续的逻辑,也即3)中的方法,也即在这里完成所有的主体逻辑。

主体的逻辑整体在:

代码语言:javascript复制
com.yomahub.liteflow.core.FlowExecutor#doExecute

这个方法里面。

因此我们对3)进行着重查看和说明。

三、chain.execute(slotIndex)的实现

代码语言:javascript复制
    //获取chain,根据chainId获取
    Chain chain = null;
    try {
        chain = FlowBus.getChain(chainId);
        if (ObjectUtil.isNull(chain)) {
            String errorMsg = StrUtil.format("couldn't find chain with the id[{}]", chainId);
            throw new ChainNotFoundException(errorMsg);
        }
        // 执行chain,也即设置槽id 对应chainId
        chain.execute(slotIndex);
    }
1)从chain到node的过程

可以看到首先会拿到chain,因为之前我们在2)完成了chain数据的填充,将将chain数据放入到了chainMap中。因此此时必然可以拿到chain的信息,然后基于chain调用节点执行器执行操作chain.execute(slotIndex)。Condition执行condition操作。当前所在的ChainName 如果对于子流程来说,那这个就是子流程所在的Chain。可以看到此时的executeCondition会执行对应的condition对应的chain。这里以串行器为例子进行说明,因此此时会进入到ThenCondition中。可以看到ThenConditon的executeCondition中存在前置preCondition.execute(slotIndex)、具体的处理executableItem.execute(slotIndex)、finally处理finallyCondition.execute(slotIndex)。此时可以看到对应的方法有三个:chain、condition、node。可以看到此时会执行节点node操作,因为前两者已经执行过。

2)node执行逻辑

设置槽索引和引用节点。,如果当前的实例可以access,则获取节点执行器,然后执行节点执行器。来到我们的执行器入口方法com.yomahub.liteflow.flow.executor.NodeExecutor#execute。这个方法可以看到,分为前置、执行中、后置方法。作者想得非常的周到,留下了扩展。除此之外,作者还给我们设置了失败重试的方法。节点组件的核心方法com.yomahub.liteflow.core.NodeComponent#execute。

在这个方法里面,会创建cmpStep对象,然后设置tag、step的信息。然后执行前置、具体的业务逻辑处理self.process(),此时会业务系统的组件方法中,也即我们自己业务系统写的NodeComponent方法,执行完成会执行成功的回调方法,如果出现异常,则设置step信息,然后执行失败回调方法。最终进行后置方法设置时间消耗。将指标统计信息添加到monitorBus中。

3)执行顺序与test中的设置对应关系

也即从这里可以看到首先会执行chain.execute(slotIndex)->executableItem.execute(slotIndex)->NodeComponent#execute,也即先执行chain,然后chain到对应的condition,最终到对应的node。也即应证了上面设置的顺序。先节点,然后condition,最终到chain。

四、实现的逻辑,可以总结如下UML

参考:https://gitee.com/dromara/liteFlow

https://liteflow.cc/

0 人点赞