LiteFlow规则引擎的入门

news2024/11/22 5:03:30

文章目录

        • 1、LiteFlow简介
        • 2、解决的痛点
        • 3、快速开始
          • 3.1 引入依赖
          • 3.2 配置规则文件的位置
          • 3.3 定义组件
          • 3.4 指定规则
          • 3.5 编写客户端
          • 3.6 运行以及说明
          • 3.7 其他的组件
        • 4、对于快速开始的思考
        • 5、LiteFlow的脚本组件
          • 5.1 脚本的定义
          • 5.2 脚本的使用
          • 5.3 关于脚本使用的思考
        • 6、规则引擎的配置源
          • 6.1 引入依赖
          • 6.2 配置参数
          • 6.3 配置apollo环境
          • 6.4 Apollo创建名称空间
          • 6.5 项目启动查看运行结果
          • 6.6 测试app.id是否有效

1、LiteFlow简介

LiteFlow是一个非常强大的现代化的规则引擎框架,融合了编排特性和规则引擎的所有特性。

利用LiteFlow,你可以将瀑布流式的代码,转变成以组件为核心概念的代码结构,这种结构的好处是可以任意编排,组件与组件之间是解耦的,组件可以用脚本来定义,组件之间的流转全靠规则来驱动。LiteFlow拥有开源规则引擎最为简单的DSL语法。十分钟就可上手。

详细可见官网:https://liteflow.yomahub.com/

2、解决的痛点

  • 复杂的业务逻辑、多变的条件的系统,维护困难、测试困难,导致不愿意有人维护项目。以至于不得不修改业务逻辑的时候,都是通过打补丁的方式,导致系统更加臃肿、一出问题难以排查。业务的多变导致小版本的不断迭代,其他开发人员接手项目时,感觉压力山大,心中一万只羊驼奔腾而过…

  • 业务多变的条件,可以随时修改,而不改变代码本身。比如根据价格的不同的区间段,返回不同的折扣。

这样的系统彻底重构的话,就可以用到LiteFlow去优化。通过将复杂的逻辑和业务代码拆分成一个个独立的子单元,每一个子单元就是一个Bean,然后通过规则引擎根据需求编排每一个子单元。就像搭积木一样,最终组装成一个完整的产品,子单元还可以公用。

多变的条件,可用通过热部署的方式,随时修改分支的走向。

3、快速开始

3.1 引入依赖

以Springboot项目搭建为例

<dependency>
    <groupId>com.yomahub</groupId>
    <artifactId>liteflow-spring-boot-starter</artifactId>
    <version>{latest.version}</version>
</dependency>
3.2 配置规则文件的位置

规则文件支持3中方式:

  • xml

  • json

  • yml

以下通过xml的形式说明,笔者以为xml文件使用更方便。

主要是为了配置规则引擎的路径(xml文件的位置)。旧版本里面必须是xxx.el.xml结尾,新版本中已经取消了限制只要是.xml结尾即可

这里只配置必须的参数,其他都是默认。详细配置请参考官网

liteflow:
  #规则文件路径
  rule-source: config/flow.el.xml

3.3 定义组件

组件其实就是一个Bean,继承LiteFLow提供的不同NodeComponent。

这里以最简单的NodeComponent为例。

@LiteflowComponent 注解中的value属性就是规则文件中的名称,name是名称的别名

@LiteflowComponent(value = "a", name = "Liteflow")
public class Anode extends NodeComponent {

    @Override
    public void process() throws Exception {
        System.out.println(">>>>>>>Anode 被调用了。。。。。" + this.getRequestData() +">>>>" +this.getContextBean(Book.class));
    }
}
@LiteflowComponent("b")
public class Bnode extends NodeComponent {

    @Override
    public void process() throws Exception {
        Book contextBean = this.getContextBean(Book.class);
        contextBean.setName("my");
        System.out.println(">>>>>>>Bnode 被调用了。。。。。"+ this.getRequestData()  +"oooooooooooooooooooooooo" + contextBean);
    }
}
@LiteflowComponent("c")
public class Cnode extends NodeComponent {

    @Override
    public void process() throws Exception {
        System.out.println(">>>>>>>Cnode 被调用了。。。。。"+ this.getRequestData()  +">>>>" +this.getContextBean(Book.class));
    }
}
3.4 指定规则

也就是编写规则文件

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE flow PUBLIC  "liteflow" "liteflow.dtd">
<flow>
    <chain name="chain1">
        THEN(a, b, c);
    </chain>
</flow>

THEN 是规则文件中的关键字,表示串行执行。执行顺序:a->b-c

3.5 编写客户端

客户端中指定了一个Book的参数,是为了验证上下文的,可以忽略。

@SpringBootTest
class SimonkingLiteflowApplicationTests {

    @Resource
    private FlowExecutor flowExecutor;

    @Test
    void contextLoads() {
        Book book = new Book();
        book.setName("wsss:" + Thread.currentThread().getName());
        LiteflowResponse response = flowExecutor.execute2Resp("chain1", "arg****************", book);
        System.out.println("response:" + JSON.toJSONString(response));
    }
}
3.6 运行以及说明

可以看出按照指定的规则运行了,还打印出了执行的链路。其中[]代表别名,<>代表耗时ms。至此我们就已经入门了。哈哈哈…

3.7 其他的组件
    上面的例子我们只是用了普通组件,除了普通组件之外还有一下几种。

  • 选择组件: 关键字SWITCH,继承NodeSwitchComponent,实现processSwitch方法,表达式如下:

    <chain name="chain1">
        SWITCH(a).to(b, c);
    </chain>
    
  • 条件组件: 关键字IF...ELIF...ELSE,继承NodeIfComponent,实现processIf方法,表达式如下:

    <chain name="chain1">
        IF(x, a, b);
    </chain>
    
  • 次数循环组件:关键字FOR...DO...,继承NodeForComponent,实现processFor方法,表达式如下:

    <chain name="chain1">
        FOR(f).DO(THEN(a, b));
    </chain>
    
  • 条件循环组件:关键字WHILE...DO...,继承NodeWhileComponent,实现processWhile方法,表达式如下:

    <chain name="chain1">
        WHILE(w).DO(THEN(a, b));
    </chai
    
  • 迭代循环组件:关键字ITERATOR...DO...,继承NodeIteratorComponent,实现processIterator方法,表达式如下:

    <chain name="chain1">
        ITERATOR(x).DO(THEN(a, b));
    </chain>
    
  • 退出循环组件:关键字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、对于快速开始的思考

规则引擎的使用,我们算是已经入门了,可以使用这个框架了。但是如果应用到我们的实际业务场景中,有没有什么疑问呢?

Q: 如果有参数,参数如何传递下一个组件?

A: LiteFlow的设计理念是将所有的业务数据存放在上下文中,类似于Spring中的ApplicationContext,组件于组件之间不传递参数。这样一来,就从数据层面一定程度的解耦了。从而达到可编排的目的。

Q: 既然有上下文,上下文是怎么定义的?

A:

  • LiteFlow提供了一个默认的数据上下文的实现:DefaultContext。这个默认的实现其实里面主要存储数据的容器就是一个Map。官方建议我们自己去实现自己数据上下文。

  • 上下文的定义起其实就是我们定义的一个类,只要传进去,整个链路共享这个类型,如案例中的Book。

     flowExecutor.execute2Resp("chain1", "arg****************", book);
    
  • LiteFlow支持多个上下文,可以传递多个上下文对象

Q: 有了上下文之后,其他组件怎么获取?可以修改么?

A:

  • 定义组件的时候我们继承的Nodexxx中已经封装了方法,直接通过this调用即可

  • 多个上文的时候我们只要指定对用的class即可获取到

  • 直接对上下文对象的操作,会直接修改上下文对象。后续节点会拿到最新的上下文数

Q: 既然上下文参数可以修改,开发的时候我们应该注意什么?

A: 上下文共享且可修改,那我们要注意一下几点

  • 串行执行的时候,参数封装、校验、获取、操作的顺序,防止业务异常

  • 并行执行的时候(多线程),主要只对共享变量读操作,尽量不要写操作。防止数据错乱

  • 条件执行的时候,注意不同的跳转,对上下文的操作

Q: 在传递上下文的时候,还有一个参数是流程初始参数(官方文档有描述),它是怎么用的?

![](https://img-blog.csdnimg.cn/img_convert/7e3cfd46b53f25a7170b85ed6b3c50f0.png)

A: 这个参数是用来传递初始化参数的,全组件共享,可以不使用,我们直接将所有参数放在上下文中即可。使用的话,也有专门的获取。

this.getRequestData()

5、LiteFlow的脚本组件

LiteFlow框架目前一共支持6种脚本语言:Groovy,Javascript,QLExpress,Python,Lua,Aviator

LiteFlow采用SPI机制进行选择脚本框架来动态编译你的脚本。

官方推荐使用Groovy,因为和java语法是最接近的。以下使用Groovy 为例。

5.1 脚本的定义

脚本组件也是定义在规则文件中的,以xml为例

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE flow PUBLIC  "liteflow" "liteflow.dtd">
<flow>
    <nodes>
        <node id="e" name="普通脚本" type="script" language="groovy">
            <![CDATA[
                println("groovy脚本wsss:main")
            ]]>
        </node>
    </nodes>
</flow>

这里定义了一个e节点,name为别名, type是节点的类型,language代表使用的语言。

Type 的分类:

script:普通脚本节点,脚本里无需返回。

switch_script:选择脚本节点,脚本里需要返回选择的节点Id。

if_script:条件脚本节点,脚本里需要返回true/false。

for_script:数量循环节点,脚本里需要返回数值,表示循环次数。

while_script:条件循环节点,脚本里需要返回true/false,表示什么条件才继续循环。

break_script:退出循环节点,脚本里需要返回true/false,表示什么时候退出循环。

5.2 脚本的使用

脚本的使用就是和Java创建Bean一样,直接通过节点Id就可以,如:

<chain name="chain2">
        THEN(e);
</chain>

修改启动类,指定执行的Chain即可

flowExecutor.execute2Resp("chain2", null, null);

5.3 关于脚本使用的思考

Q: 如果脚本定义的节点和Java定义的节点Id一致会怎样?

A: 首先,不会报错,这是令人神奇的地方。经过测试脚本的优先级高于JavaBean。也就是相同时,会调用脚本的节点忽略JavaBean的节点

Q: 脚本的作用是啥?脚本只能单纯的定义节点么?

A: 笔者以为脚本组件为规则引擎提供了更多可能。为什么这么说呢,首先如果依靠脚本编写节点,我们是完全不用编写JavaBean就可以实现规则引擎的调用,当然这个例子有点夸张且不切实际。但是我们可以手动创建节点,并且通过节点控制流程的走向,是不是给我们提供了极大的灵活以及可能。单纯的项目可能想象不了这种灵活,因为不管怎么样我们都需要编写代码并发布。但是,如果搭配中间件(注册发现的组件),我们就可以通过修改脚本节点直接动态控制线上的业务流程的走向,且不用代码发布。这是不是很爽呢。

     脚本当然不是单纯的定义节点,他可以实现与上下文的交互,通过不同的节点类型,修改规则链路的走向。

6、规则引擎的配置源

如果所有的规则引擎文件都只是配置在项目里,那么规则引擎的另一大亮点就会被埋没。想象一下如果规则文件可以动态修改,而不用发布代码,对于多变的业务流程场景岂不是很香。官方已经提供了多种配置源:

以Apollo配置源为例

6.1 引入依赖
<dependency>
    <groupId>com.yomahub</groupId>
    <artifactId>liteflow-rule-apollo</artifactId>
    <version>{latest.version}</version>
</dependency>
6.2 配置参数
依赖插件包之后,无需再配置`liteflow.ruleSource`的路径。只允许要配置额外的参数即可:
liteflow:
  rule-source-ext-data-map
    # 执行链的namespace
    chainNamespace: liteFlow
    # 脚本组件的namespace
    scriptNamespace: liteFlow-script
6.3 配置apollo环境

默认已经安装apollo服务。windows将apollo的连接放置在C:\opt\setting\server.properties

env=DEV
apollo.meta=http://127.0.0.1:8190
idc=local

项目使用apollo为配置中心的的朋友都应该知道,项目中需要指定/META-INF/app.properties中要指定app.id和Apollo中的AppId对应。我们这里需要配置么?

官方文档没有说明要配置,我们先不配置。

6.4 Apollo创建名称空间

执行链的key为链的名称,value为具体的规则
脚本组件的key有固定格式:脚本组件ID:脚本类型:脚本名称,value为脚本数据

6.5 项目启动查看运行结果

我们看到apollo中配置的已经执行了,和项目中的配置一模一样

6.6 测试app.id是否有效

测试中发现,故意指定一个app.id,项目不会报错并且不会影响正常的链路调用,也就是说namespace是整个集群(local:因为我server.properties中指定idc=local)共享的。app.id配置或者不配置、甚至配错都不会影响规则引擎的执行。

– END

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/450555.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

开源Qt Ribbon控件——SARibbon的布局思路及介绍

开源Qt Ribbon控件——SARibbon的布局思路及介绍 SARibbon的布局SARibbon名词定义Office布局模式——SARibbonBar::OfficeStyleWPS布局模式——SARibbonBar::WpsLiteStylepannel的布局行数3行模式2行模式 原文链接&#xff1a;https://blog.csdn.net/czyt1988/article/details/…

scala之基础面向对象

scala 既是面向对象 也是函数式编程 从Java 发展而来&#xff0c;依赖JVM环境 一、 scala 在linux中运行 scala 模式中直接编写运行 scala文件&#xff0c;load执行 scala编译程序 编译 运行 scala java 二、scala 数据类型 基础数据类型 val 不可变变量 函数式编程 …

Excel使用频率超高的20个函数,90%你没用过

上班必学必会的Excel函数&#xff0c;不仅是使用频率最大的&#xff0c;还是告别加班的利器。你会的函数越多&#xff0c;解决问题的思路越广&#xff0c;不再束手束脚。态度决定高度&#xff0c;细节决定成败。要想比别人更优秀&#xff0c;只有在每一件小事上比功夫。 组合、…

json-c交叉编译及库移植

编译后的文件 json-c交叉编译及库移植资源-CSDN文库 json-c开源库是一个用c实现的解析json字段的库&#xff0c;嵌入式领域比较实用的库。 由于应用程序需要&#xff0c;需要找移植这个json-c库&#xff0c;所以这里对该库的移植做个简单说明 json-c开源库是一个用c实现的解…

python基于机器学习模型开发实践kaggle旧金山犯罪案件分类预测模型

旧金山犯罪案件分类本质是一个文本的多分类任务&#xff0c;kaggle官网地址在这里&#xff0c;如下所示&#xff1a; 本文主要是以kaggle比赛数据集为基准&#xff0c;开发实践文本多分类任务。 比赛背景 从 1934 年到 1963 年&#xff0c;旧金山因高犯罪率而臭名昭著。时至今…

opengl绘制三角形

1.绘制两个三角形 GLfloat vertices1[] { 0.5f, 0.5f, 0.0f, 0.5f, -0.5f, 0.0f, -0.5f, 0.5f, 0.0f } GLfloat vertices2[] { 0.5f, -0.5f, 0.0f, -0.5f, 0.5f, 0.0f&#xff0c; -0.5f, -0.5f, 0.0f } 也可以用索引的方式&#xff1a; GLfloat vertices[] { 0.5f, 0.5f, 0…

并发编程常见问题复盘

并发编程常见问题复盘 大家好&#xff0c;我是易安&#xff01; 并发编程在计算机科学领域占有举足轻重的地位&#xff0c;它使得程序能够在多个处理器核心上同时执行&#xff0c;从而显著提升程序的性能。然而&#xff0c;并发编程也伴随着许多挑战和问题。这些年来&#xff0…

eacharjs饼状图带百分比

var myChart1 echarts.init(document.getElementById(main1)); myChart1.setOption({title:{text:近30天异常停机的类型TOP5,x:center,y:10px,// textStyle:{// fontSize:12// }},tooltip: {trigger: item//提示 鼠标移动上去},// legend: { // 上面的提示// top: 25%…

端口映射工具PortTunnel

PortTunnel应该是目前最好的端口转发器、端口映射工具(它解决了内外网访问的问题) 可以在我的资源中下载&#xff1a;https://download.csdn.net/download/qq_39569480/87717704 使用该工具前应该保证双方机器网络互通 下面我们模拟一下环境 比如现在有三台机器 A&#xff1a…

Mac环境SpringBoot项目Docker部署(独家完整版)

一、Docker 简介 Docker 是一种开源的容器化平台&#xff0c;允许开发人员将应用程序和所有其依赖项打包成轻量级、可移植的容器&#xff0c;以便在任何地方运行。Docker 的优势和劣势分析如下&#xff1a; 优势: 轻量级:Docker 容器仅包含应用程序及其依赖项&#xff0c;因…

家庭智能吸顶灯一Homekit智能

买灯要看什么因素 好灯具的灯光可以说是家居的“魔术师”&#xff0c;除了实用的照明功能外&#xff0c;对细节的把控也非常到位。那么该如何选到一款各方面合适的灯呢&#xff1f; 照度 可以简单理解为清晰度&#xff0c;复杂点套公式来说照度光通量&#xff08;亮度&#x…

【社区图书馆】二、LED子系统——硬件驱动层

个人主页&#xff1a;董哥聊技术 我是董哥&#xff0c;嵌入式领域新星创作者 创作理念&#xff1a;专注分享高质量嵌入式文章&#xff0c;让大家读有所得&#xff01; 文章目录 1、gpio_led_probe分析1.1 相关数据结构1.1.1 gpio_led_platform_data1.1.2 gpio_leds_priv 1.2 实…

Nextjs 处理 css3 前缀兼容

Nextjs 处理 css3 前缀兼容 虽然css3现在浏览器支持率已经很高了, 但有时候需要兼容一些低版本浏览器,需要给css3加前缀,可以借助插件来自动加前缀, postcss-loader就是来给css3加浏览器前缀的,安装依赖&#xff1a; npm i postcss-loader autoprefixer -Dpostcss-loader&…

前端使用国密SM4进行加密、解密

目录 需求【方法1】 - 使用 sm4util 依赖【方法2】sm4.js引入1. /public/sm4.js2. body 标签上引入该文件3. 使用 - ECB 模式加密 【方法3】1. 本地写 js 文件2. 使用 - ECB 模式加解密 需求 前端/后端使用 国密SM4 进行加密/解密&#xff0c; 【注意】前后端配合加解密时&…

06期:使用 OPTIMIZER_TRACE 窥探 MySQL 索引选择的秘密

这里记录的是学习分享内容&#xff0c;文章维护在 Github&#xff1a;studeyang/leanrning-share。 优化查询语句的性能是 MySQL 数据库管理中的一个重要方面。在优化查询性能时&#xff0c;选择正确的索引对于减少查询的响应时间和提高系统性能至关重要。但是&#xff0c;如何…

scrapy框架爬取某壁纸网站美女壁纸 + MySQL持久化存储

文章目录 准备工作创建项目&#xff1a;设置&#xff08;settings&#xff09; 主程序入口meinv.py思路源代码 items 配置管道pipelines源代码 效果图总结 准备工作 创建项目&#xff1a; scraoy startproject bizhi cd bizhi scrapy genspider meinv bizhi360.com 设置&#…

ROS学习第二十九节——URDF之joint

此处留疑问&#xff0c;link,joint的origin子标签到底是怎么样的一种位置关系&#xff1f;&#xff1f;&#xff1f; https://download.csdn.net/download/qq_45685327/87717336 urdf 中的 joint 标签用于描述机器人关节的运动学和动力学属性&#xff0c;还可以指定关节运动的…

大数据-玩转数据-IDEA创建Maven工程

一、 IDEA集成Maven插件 打开IDEA&#xff0c;进入主界面后点击 file&#xff0c;然后点击 settings,在上面的快捷查找框中输入maven&#xff0c;查找与maven相关的设置&#xff0c;然后点击maven 修改maven的路径&#xff08;使用本地的Maven&#xff09;&#xff0c;以及修…

【流畅的Python学习笔记】2023.4.22

此栏目记录我学习《流畅的Python》一书的学习笔记&#xff0c;这是一个自用笔记&#xff0c;所以写的比较随意 元组 元组其实是对数据的记录&#xff1a;元组中的每个元素都存放了记录中一个字段的数据&#xff0c;外加这个字段的位置。简单试试元组的特性&#xff1a; char…

kong(1):Kong介绍

Kong是一款基于OpenResty&#xff08;Nginx Lua模块&#xff09;编写的高可用、易扩展的&#xff0c;由Mashape公司开源的API Gateway项目。Kong是基于NGINX和Apache Cassandra或PostgreSQL构建的&#xff0c;能提供易于使用的RESTful API来操作和配置API管理系统&#xff0c;…