1、前言

上一章我们讲了入门的LiteFlow的使用,都是写好在代码里面的,规则也硬编码再工程里面,如果某一天我想改一下这个规则顺序只能拉代码再写、再改、再发布。这种if-else就能搞定的东西,我还用你这鬼动干啥?

这不,LiteFlow就帮我们实现了这种动态规则的支持,还可以动态写每一个节点的逻辑代码。这一章我们来着重讲一下如何把规则和脚本放在第三方存储,动态变更规则或脚本让现实使用更灵活。

2、动态规则和脚本

我选择的是apollo作为第三方存储,其他的mysql或者redis、zk等各位自行参考官网文档。动手之前先说一下这两个名称:规则、脚本。

**规则:**就是前面配置的chain1、chain2这些配置流转条件THEN(a, b, c);

**脚本:**就是我们如果新增定义一个Node节点代码d,正常应该在我们的工程里面写java代码:

@Component("d")
public class DCmp extends NodeComponent {
    @Override
    public void process() {
        System.out.println("DCmp");
    }
}

动态的脚本就是让我们不用在工程里面写代码,我们的代码随时可以写在apollo中,有LiteFlow动态加载为一个规则节点,这就是动态脚本,但官网不太期望我们用这种模式,建议如果没有太多变化的,还是老老实实写好java代码去使用。这种动态的可能会有加载问题或者其他不可控因素。

不过作为学习,我们可以学这种动态java脚本是怎么实例化的,倒是非常好的学习示例。

2.1 引入依赖

我们用的是apollo作为第三方存储,那就要依赖这个包

<dependency>
    <groupId>com.yomahub</groupId>
    <artifactId>liteflow-rule-apollo</artifactId>
    <version>{latest.version}</version>
</dependency>

在自己的apollo项目新增两个命名空间,必须两个专用的命名空间,否则启动加载会校验规则和脚本是否正确可能会无法正常启动。

| 配置项          | 说明             |
| --------------- | ---------------- |
| chainNamespace  | 规则命名空间名称 |
| scriptNamespace | 脚本命名空间名称 |
# 在application.properties里面加这个配啊,指定命名空间
liteflow.rule-source-ext-data={\
    "chainNamespace":"chainConfig",\
    "scriptNamespace":"scriptConfig"\
}

我觉得他这个命名规范很有问题,主key就叫liteflow.rule-source-ext-data,都无法区分你到底用的什么第三方存储。

2.2 动态规则

我们第一步先玩一下规则,值用到chainNamespace命名空间即可,我们在里面配置一下上一个实例的chain1和chain2

chain2 = THEN(a, b);
chain1 = THEN(a, c);

我自己做了一个Controller,传参方式去调用指定的chain2,chain1,随时去apollo改这个规则,发现确实能动态变化

@Controller
@RequestMapping(value = "/liteflow")
public class ExecuteController {
    @Resource
    private FlowExecutor flowExecutor;

    @RequestMapping(value = "/exec")
    public @ResponseBody String exec(@RequestParam(name = "chainId") String chainId) {
        LiteflowResponse response = flowExecutor.execute2Resp(chainId, "arg");
        // 执行LiteFlow的逻辑
        return response.getMessage();
    }
}
2024-03-27 21:35:39.782  INFO [io2-8082-exec-9] [TID: N/A|df7e074826054d53879e8976a6f19bb3] c.y.l.f.e.Node                           : [20ace27cde5047388c9b0655f8fe4a6b]:[O]start component[a] execution
ACmp
2024-03-27 21:35:39.829  INFO [io2-8082-exec-9] [TID: N/A|df7e074826054d53879e8976a6f19bb3] c.y.l.f.e.Node                           : [20ace27cde5047388c9b0655f8fe4a6b]:[O]start component[b] execution
BCmp
2024-03-27 21:35:39.829  INFO [io2-8082-exec-9] [TID: N/A|df7e074826054d53879e8976a6f19bb3] c.y.l.f.e.Node                           : [20ace27cde5047388c9b0655f8fe4a6b]:[O]start component[c] execution
BCmp
2024-03-27 21:35:39.832  INFO [io2-8082-exec-9] [TID: N/A|df7e074826054d53879e8976a6f19bb3] c.y.l.s.Slot                             : [20ace27cde5047388c9b0655f8fe4a6b]:CHAIN_NAME[chain2]
a<10>==>b<0>==>c<0>
2024-03-27 21:35:39.832  INFO [io2-8082-exec-9] [TID: N/A|df7e074826054d53879e8976a6f19bb3] c.y.l.s.DataBus                          : [20ace27cde5047388c9b0655f8fe4a6b]:slot[1] released

2.3 动态脚本

动态脚本在官网示例是有点坑的,没说清楚这个脚本是用什么语言,官网的脚本其实默认是用groovy做示例,在依赖支持脚本语言的包上也没说清楚。

| Namespace:scriptConfig |                                              |
| ----------------------- | -------------------------------------------- |
| s1:script:脚本组件1     | defaultContext.setData("s1","hello")         |
| s2:if_script:脚本组件2  | if(a > 100){return true;}else{return false;} |

我第一眼看的时候我想用java写,好像怪怪的,key有固定格式:脚本组件ID:脚本类型:脚本名称,value为脚本数据,这句话其实很有歧义,是因为他支持很多种语言,但是你咋知道我写的这个脚本是java还是python还是啥。

第二个坑,我是用java写的脚本,启动报错。而且他有没有java的第三方存储示例,只有xml配置的方式示例,我搞了个把小时才把坑填完,给大家演示一下。

依赖

<dependency>
  <groupId>com.yomahub</groupId>
  <artifactId>liteflow-script-java</artifactId>
  <version>{latest.version}</version>
</dependency>

启动报错

An attempt was made to call a method that does not exist. The attempt was made from the following location:

    com.yomahub.liteflow.script.java.JavaExecutor.load(JavaExecutor.java:22)

The following method did not exist:

    org.codehaus.commons.compiler.CompilerFactoryFactory.getDefaultCompilerFactory(Ljava/lang/ClassLoader;)Lorg/codehaus/commons/compiler/ICompilerFactory;

The method's class, org.codehaus.commons.compiler.CompilerFactoryFactory, is available from the following locations:

    jar:file:/Users/cookie.joo/.m2/repository/org/codehaus/janino/commons-compiler/3.0.12/commons-compiler-3.0.12.jar!/org/codehaus/commons/compiler/CompilerFactoryFactory.class

It was loaded from the following location:

    file:/Users/cookie.joo/.m2/repository/org/codehaus/janino/commons-compiler/3.0.12/commons-compiler-3.0.12.jar


Action:

Correct the classpath of your application so that it contains a single, compatible version of org.codehaus.commons.compiler.CompilerFactoryFactory

原因是有个janino包是版本不支持,这个不知道和什么包依赖导致的,升级到指定的版本即可。一开始报这个错还以为我写的java脚本不对还是啥的,整理半天去gitee看issues才知道要升级这个包。

<dependency>
  <groupId>org.codehaus.janino</groupId>
  <artifactId>janino</artifactId>
  <version>3.1.12</version>
</dependency>

如果你用的是java的脚本,那就这样依赖进来就可以了,然后你新增一个d节点key为:d:script:脚本组件1,值为:

import com.yomahub.liteflow.script.body.JaninoCommonScriptBody;
import com.yomahub.liteflow.script.ScriptExecuteWrap;

public class TestCmp implements JaninoCommonScriptBody {
    public Void body(ScriptExecuteWrap wrap) {
        System.out.println("caodegao");
        return null;
    }
}

就是把完整的java文件内容放到apollo的value值中即可,需要在idea里面把代码写好,再复制apoll上来用。执行chain1 的逻辑配置:chain1 = THEN(a, c, s1); 后会打印caodegao出来。以上就是动态规则和脚本的示例。还有更高深的玩法各位参考官网示例,无非就那几个常用的条件分支玩法,有几种不同的接口实现不同的逻辑

2.4 脚本语言的选择

动态脚本语言的选择我暂时也没看出什么门道来,假如我要用groovy和java一起来写我的动态脚本代码,我也依赖了groovy的支持包,LiteFlow如何选择的,这个我还没搞清楚。我发现两个依赖都给了,然后java的初始化我断点并没有进来,就groovy的断点进来的。

所以我猜测默认只会用groovy的,你加多个脚本依赖的话就只会激活groovy。大家要用groovy作为动态脚本的话就只依赖groovy即可,大家可以用官网的那个示例玩groovy脚本。

<dependency>
  <groupId>com.yomahub</groupId>
  <artifactId>liteflow-script-java</artifactId>
  <version>{latest.version}</version>
</dependency>

<dependency>
  <groupId>com.yomahub</groupId>
  <artifactId>liteflow-script-groovy</artifactId>
  <version>{latest.version}</version>
</dependency>

如果各位用java写的比较顺手,那就只依赖java的支持即可,不用全都依赖进来。这点我估计LiteFlow的设计也没考虑这个事,至少官网文档啥都没说。

3、最后

我们试玩了一下LiteFlow动态脚本的有意思的地方,但是我很好奇他是怎么动态加载一个java代码到内存里面的,怎么做到规则可以动态刷新的,我找时间研究一下源码,下一章再分享一下这个黑科技的实现原理。

上一篇 下一篇