SpringBoot中集成LiteFlow(轻量、快速、稳定可编排的组件式规则引擎)实现复杂业务解耦、动态编排、高可扩展

news2024/11/25 18:35:11

场景

在业务开发中,经常遇到一些串行或者并行的业务流程问题,而业务之间不必存在相关性。

使用策略和模板模式的结合可以解决这个问题,但是使用编码的方式会使得文件太多,

在业务的部分环节可以这样操作,在项目角度就无法一眼洞穿其中的环节和逻辑。

一些拥有复杂业务逻辑的系统,核心业务逻辑冗长,涉及内部逻辑运算,缓存操作,持久化操作,外部资源调取,内部其他系统RPC调用等等。

时间一长,维护的成本就会越来越高。各种硬代码判断,分支条件越来越多。代码的抽象,复用率也越来越低,各个模块之间的耦合度很高。

一小段逻辑的变动,会影响到其他模块,需要进行完整回归测试来验证。

如要灵活改变业务流程的顺序,则要进行代码大改动进行抽象,重新写方法。

实时热变更业务流程,几乎很难实现 。

LiteFlow

LiteFlow就是为解耦复杂逻辑而生,如果你要对复杂业务逻辑进行新写或者重构,用LiteFlow最合适不过。

它是一个轻量,快速的组件式流程引擎框架,组件编排,帮助解耦业务代码,让每一个业务片段都是一个组件,

并支持热加载规则配置,实现即时修改。

使用LiteFlow,你需要去把复杂的业务逻辑按代码片段拆分成一个个小组件,并定义一个规则流程配置。

这样,所有的组件,就能按照你的规则配置去进行复杂的流转。

LiteFlow官方网站:

LiteFlow

LiteFlow的Gitee地址:

liteFlow: 轻量,快速,稳定,可编排的组件式规则引擎/流程引擎。拥有全新设计的DSL规则表达式。组件复用,同步/异步编排,动态编排,支持超多语言脚本,复杂嵌套规则,热部署,平滑刷新规则等等功能,让你加快开发效率!

LiteFlow的特点:

注:

博客:
霸道流氓气质-CSDN博客

实现

1、SpringBoot中集成LiteFlow

LiteFlow要求的Springboot的最低的版本是2.0。

支持的范围是Springboot 2.X ~ Springboot 3.X。

LiteFlow提供了liteflow-spring-boot-starter依赖包,提供自动装配功能

<dependency>
    <groupId>com.yomahub</groupId>
    <artifactId>liteflow-spring-boot-starter</artifactId>
    <version>2.11.4.2</version>
</dependency>

2、SpringBoot中配置LiteFlow

在你的SpringBoot的application.properties或者application.yml里添加配置

#liteflow规则配置文件位置
liteflow:
  rule-source: config/flow.el.xml

规则文件的定义

在resources下的config/flow.el.xml中定义规则:

<?xml version="1.0" encoding="UTF-8"?>
<flow>
    <chain name="chain1">
        THEN(acmp, bcmp, ccmp);
    </chain>
</flow>

根据定义的规则,需要定义并实现一些组件,确保SpringBoot会扫描到这些组件并注册进上下文。

import com.yomahub.liteflow.core.NodeComponent;
import org.springframework.stereotype.Component;

@Component("acmp")
public class ACmp extends NodeComponent {

    @Override
    public void process() {
        //do your business
        System.out.println("acmp执行");
    }
}

以此类推,定义另外两个组件

import com.yomahub.liteflow.core.NodeComponent;
import org.springframework.stereotype.Component;

@Component("bcmp")
public class BCmp extends NodeComponent {

    @Override
    public void process() {
        //do your business
        System.out.println("bcmp执行");
    }
}
import com.yomahub.liteflow.core.NodeComponent;
import org.springframework.stereotype.Component;

@Component("ccmp")
public class CCmp extends NodeComponent {

    @Override
    public void process() {
        //do your business
        System.out.println("ccmp执行");
    }
}

更多配置项内容参考文档说明

🌿Springboot下的配置项 | LiteFlow

liteflow:
  #规则文件路径
  rule-source: config/flow.el.xml
  #-----------------以下非必须-----------------
  #liteflow是否开启,默认为true
  enable: true
  #liteflow的banner打印是否开启,默认为true
  print-banner: true
  #zkNode的节点,只有使用zk作为配置源的时候才起作用,默认为/lite-flow/flow
  zk-node: /lite-flow/flow
  #上下文的最大数量槽,默认值为1024
  slot-size: 1024
  #FlowExecutor的execute2Future的线程数,默认为64
  main-executor-works: 64
  #FlowExecutor的execute2Future的自定义线程池Builder,LiteFlow提供了默认的Builder
  main-executor-class: com.yomahub.liteflow.thread.LiteFlowDefaultMainExecutorBuilder
  #自定义请求ID的生成类,LiteFlow提供了默认的生成类
  request-id-generator-class: com.yomahub.liteflow.flow.id.DefaultRequestIdGenerator
  #并行节点的线程池Builder,LiteFlow提供了默认的Builder
  thread-executor-class: com.yomahub.liteflow.thread.LiteFlowDefaultWhenExecutorBuilder
  #异步线程最长的等待时间(只用于when),默认值为15000
  when-max-wait-time: 15000
  #异步线程最长的等待时间(只用于when),默认值为MILLISECONDS,毫秒
  when-max-wait-time-unit: MILLISECONDS
  #when节点全局异步线程池最大线程数,默认为16
  when-max-workers: 16
  #并行循环子项线程池最大线程数,默认为16
  parallelLoop-max-workers: 16
  #并行循环子项线程池等待队列数,默认为512
  parallelLoop-queue-limit: 512
  #并行循环子项的线程池Builder,LiteFlow提供了默认的Builder
  parallelLoop-executor-class: com.yomahub.liteflow.thread.LiteFlowDefaultParallelLoopExecutorBuilder
  #when节点全局异步线程池等待队列数,默认为512
  when-queue-limit: 512
  #是否在启动的时候就解析规则,默认为true
  parse-on-start: true
  #全局重试次数,默认为0
  retry-count: 0
  #是否支持不同类型的加载方式混用,默认为false
  support-multiple-type: false
  #全局默认节点执行器
  node-executor-class: com.yomahub.liteflow.flow.executor.DefaultNodeExecutor
  #是否打印执行中过程中的日志,默认为true
  print-execution-log: true
  #是否开启本地文件监听,默认为false
  enable-monitor-file: false
  #是否开启快速解析模式,默认为false
  fast-load: false
  #简易监控配置选项
  monitor:
    #监控是否开启,默认不开启
    enable-log: false
    #监控队列存储大小,默认值为200
    queue-limit: 200
    #监控一开始延迟多少执行,默认值为300000毫秒,也就是5分钟
    delay: 300000
    #监控日志打印每过多少时间执行一次,默认值为300000毫秒,也就是5分钟
    period: 300000

3、SpringBoot中执行LiteFlow

声明启动类,确保定义的组件扫入Spring上下文

@SpringBootApplication
//把你定义的组件扫入Spring上下文中
@ComponentScan({"com.xxx.xxx.cmp"})
public class LiteflowExampleApplication {

    public static void main(String[] args) {
        SpringApplication.run(LiteflowExampleApplication.class, args);
    }
}

然后可以在在Springboot任意被Spring托管的类中拿到flowExecutor,进行执行链路

这里进行单元测试

import com.yomahub.liteflow.core.FlowExecutor;
import com.yomahub.liteflow.flow.LiteflowResponse;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import javax.annotation.Resource;

@RunWith(SpringRunner.class)
@SpringBootTest(classes = RuoYiApplication.class,webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class LiteFlowTest {

    @Resource
    private FlowExecutor flowExecutor;

    @Test
    public void helloLiteFlow() {
        LiteflowResponse response = flowExecutor.execute2Resp("chain1");
        System.out.println(response);
    }
}

这里的chain1与上面规则文件中的对应。

运行结果

可以看到三个组件依次执行,这是因为配置的规则文件中配置的规则如此。

除了上面配置的普通组件之外,还可配置其他组件,比如选择组件、条件组件、循环组件等

📎普通组件 | LiteFlow

下面配置一个选择组件为例

在实际业务中,往往要通过动态的业务逻辑判断到底接下去该执行哪一个节点,这就引申出了选择节点,

选择节点可以用于SWITCH关键字中。

关于SWITCH表达式的用法,可以参考选择编排一章。

选择节点a需要继承NodeSwitchComponent。

需要实现方法processSwitch方法

import com.yomahub.liteflow.core.NodeSwitchComponent;
import org.springframework.stereotype.Component;

@Component("switchCmp")
public class SwitchCmp extends NodeSwitchComponent {

    @Override
    public String processSwitch() throws Exception {
        System.out.println("switchCmp executed!");
        //自己业务选择
        //以下代表选择了switchCmpA节点
        return "switchCmpA";
    }
}

配置规则文件

<?xml version="1.0" encoding="UTF-8"?>
<flow>
    <chain name="chain1">
        THEN(acmp, bcmp, ccmp);
    </chain>
    <chain name="switch_chain">
        SWITCH(switchCmp).to(switchCmpA, switchCmpB);
    </chain>
</flow>

其中switchCmpA与switchCmpB是普通组件

编写单元测试

    @Test
    public void switchTest() {
        LiteflowResponse response = flowExecutor.execute2Resp("switch_chain");
    }

运行结果

条件组件用法

LiteFlow从2.8.5版本开始,提供了条件组件的定义。

条件组件,也可以称之为IF组件,返回是一个true/false。可用于IF...ELIF...ELSE等关键字。

关于IF...ELIF...ELSE表达式的用法,可以参考条件编排这一章。

比如一个IF三元表达式,如下所示,xcmp就是IF组件,为真,执行acmp,为假,执行bcmp:

    <chain name="if_chain">
        IF(xcmp, acmp, bcmp);
    </chain>

编写条件组件

import com.yomahub.liteflow.core.NodeIfComponent;
import org.springframework.stereotype.Component;

@Component("xcmp")
public class XCmp extends NodeIfComponent {

    @Override
    public boolean processIf() throws Exception {
        //自己的业务判断
        return false;
    }
}

其它更多组件用法参考官方文档。

4、LiteFlow组件传参

在一个流程中,总会有一些初始的参数,比如订单号,用户Id等等一些的初始参数。

这时候需要通过以下方法的第二个参数传入

public LiteflowResponse execute2Resp(String chainId, Object param, Class<?>... contextBeanClazzArray)

这个流程入参,可以是任何对象,一般生产业务场景下,你可以把自己封装好的Bean传入。

编写传参组件

import com.ruoyi.system.domain.BusStudent;
import com.yomahub.liteflow.core.NodeIfComponent;
import org.springframework.stereotype.Component;

@Component("xpcmp")
public class XParamCmp extends NodeIfComponent {

    @Override
    public boolean processIf() throws Exception {
        //自己的业务判断
        BusStudent requestData = this.getRequestData();
        if(null!=requestData.getName()&&"公众号:霸道的程序猿".equals(requestData.getName())){
            return true;
        }else{
            return false;
        }
    }
}

传参使用

flowExecutor.execute2Resp("if_param_chain", BusStudent.builder().name("公众号:霸道的程序猿").build());

5、LiteFlow声明式组件

普通组件和条件组件,在写法上需要你自己去定义一个类去继承NodeComponent或者NodeSwitchComponent。

这样一方面造成了耦合,另一方面由于java是单继承制,所以使用者就无法再去继承自己的类了,在自由度上就少了很多玩法。

声明式组件这一特性允许你自定义的组件不继承任何类和实现任何接口,普通的类也可以依靠注解来完成LiteFlow组件的声明。

甚至于你可以用一个类去定义多个组件,仅仅依靠注解就可以完成,这个特性也叫做方法级别式声明

类级别式声明主要用处就是通过注解形式让普通的java bean变成LiteFlow的组件。无需通过继承类或者实现接口的方式。

由于LiteFlow的组件常规方式下需要继承类来定义,使得你无法再继承自己业务的类了。这个特性可以解决这个问题。

但是和常规组件一样,需要一个类对应一个组件

自定义一个组件并使用方法级别声明

import com.yomahub.liteflow.annotation.LiteflowCmpDefine;
import com.yomahub.liteflow.annotation.LiteflowComponent;
import com.yomahub.liteflow.annotation.LiteflowMethod;
import com.yomahub.liteflow.core.NodeComponent;
import com.yomahub.liteflow.enums.LiteFlowMethodEnum;

@LiteflowComponent("defineCmp")
@LiteflowCmpDefine
public class DefineCmp{
    @LiteflowMethod(LiteFlowMethodEnum.PROCESS)
    public void processAcmp(NodeComponent bindCmp) {
        System.out.println("ACmp executed!");
    }

    @LiteflowMethod(LiteFlowMethodEnum.IS_ACCESS)
    public boolean isAcmpAccess(NodeComponent bindCmp){
        return true;
    }

    @LiteflowMethod(LiteFlowMethodEnum.BEFORE_PROCESS)
    public void beforeAcmp(NodeComponent bindCmp){
        System.out.println("before A");
    }

    @LiteflowMethod(LiteFlowMethodEnum.AFTER_PROCESS)
    public void afterAcmp(NodeComponent bindCmp){
        System.out.println("after A");
    }

    @LiteflowMethod(LiteFlowMethodEnum.ON_SUCCESS)
    public void onAcmpSuccess(NodeComponent bindCmp){
        System.out.println("Acmp success");
    }

    @LiteflowMethod(LiteFlowMethodEnum.ON_ERROR)
    public void onAcmpError(NodeComponent bindCmp, Exception e){
        System.out.println("Acmp error");
    }

    @LiteflowMethod(LiteFlowMethodEnum.IS_END)
    public boolean isAcmpEnd(NodeComponent bindCmp) {
        return false;
    }

    @LiteflowMethod(value = LiteFlowMethodEnum.ROLLBACK)
    public void rollbackA(NodeComponent bindCmp) throws Exception {
        System.out.println("ACmp rollback!");
    }
}

同样实现效果。

6、LiteFlow还有更多功能和属性

比如EL规则的编排

🍄说明 | LiteFlow

用代码动态构造规则

🍄说明 | LiteFlow

以及各种给高级特性、平滑热更新等。

具体参考官方文档说明。

DEMO案例

滑动验证页面

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

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

相关文章

Keil软件无法烧录程序的解决方案

1.由于单片机程序有些情况下出错&#xff0c;导致烧录进去单片机运行异常&#xff0c;无法烧录程序&#xff0c;但是Keil软件可以识别到SW Device器件&#xff0c;点击烧录程序提示no target connected连接。 解决方案: (1).点击魔术棒->debug->Settings&#xff0c;选择…

网络编程day6

1.思维导图 2.数据库操作的增、删、改完成。 #include<myhead.h> //定义新增员工信息函数 int do_add(sqlite3 *ppDb) {int numb;char name;double salary;printf("请输入要插入的信息&#xff1a;");scanf("%d%s%d\n",&numb,name,&salary)…

7大必备应用推荐,为你的 Nextcloud 实例增添更多效率功能

适用于 Linux 的开源云存储软件有很多&#xff0c;ownCloud、Seafile 和 Pydio 只是其中的几个。 不过&#xff0c;如果您非常重视安全问题&#xff0c;并希望完全掌管您的数据&#xff0c;可以选择​Nextcloud并将其安装到您的服务器上。​ Nextcloud 是一个基于 PHP 的开源安…

Pytest中实现自动生成测试用例脚本代码!

前言 在Python的测试框架中&#xff0c;我们通常会针对某个系统进行测试用例的维护&#xff0c;在对庞大系统进行用例维护时&#xff0c;往往会发现很多测试用例是差不多的&#xff0c;甚至大多数代码是一样的。 故为了提高我们测试用例维护的效率&#xff0c;在本文中&#…

Java常用笔试题,面试java对未来的规划

最重要的话 2021年&#xff0c;真希望行业能春暖花开。 去年由于疫情的影响&#xff0c;无数行业都受到了影响&#xff0c;互联网寒冬下&#xff0c;许多程序员被裁&#xff0c;大环境格外困难。 我被公司裁掉后&#xff0c;便着急地开始找工作&#xff0c;一次次地碰壁&#…

爬虫学习笔记-requests爬取王者荣耀皮肤图片

1.导入所需的包 import requests from lxml import etree import os from time import sleep 2.定义请求头 headers {User-Agent:Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Safari/537.36} 3.发送请求 # hero…

数据结构->链表分类与oj(题),带你提升代码好感

✅作者简介&#xff1a;大家好&#xff0c;我是橘橙黄又青&#xff0c;一个想要与大家共同进步的男人&#x1f609;&#x1f609; &#x1f34e;个人主页&#xff1a;橘橙黄又青-CSDN博客 1.&#x1f34e;链表的分类 前面我们学过顺序表&#xff0c;顺序表问题&#xff1a; …

基于springboot实现的幼儿园管理系统

一、系统架构 前端&#xff1a;html | layui | jquery | css 后端&#xff1a;springboot | mybatis 环境&#xff1a;jdk1.8 | mysql | maven 二、代码及数据库 三、功能介绍 01. 登录页 02. 系统管理-用户管理 03. 系统管理-页面管理 04. 系统管理-角色管…

喜报|3DCAT成为国内首批适配Vision Pro内容开发者

近日&#xff0c;苹果在上海总部举办了国内首场 Apple Vision Pro 开发者实验室活动&#xff0c;3DCAT作为国内领先的实时渲染云平台参与了此次活动&#xff0c;成为国内首批适配 Vision Pro 的内容开发者之一。 Vision Pro是苹果于2023年6月发布的首个空间计算设备&#xff0…

【C++STL详解 —— string类】

【CSTL详解 —— string类】 CSTL详解 —— sring类一、string的定义方式二、string的插入三、string的拼接四、string的删除五、string的查找六、string的比较七、string的替换八、string的交换九、string的大小和容量十、string中元素的访问十一、string中运算符的使用十二、…

鸿蒙NEXT开发实战:【视频文件裁剪】

使用OpenHarmony系统提供的ffmpeg三方库的能力在系统中实现了音视频文件裁剪的功能&#xff0c;并通过NAPI提供给上层应用调用。 基础信息 视频文件裁剪 简介 在OpenHarmony系统整个框架中有很多子系统&#xff0c;其中多媒体子系统是OpenHarmony比较重要的一个子系统&#…

Java+SpringBoot+Vue+MySQL:农业管理新篇章

✍✍计算机毕业编程指导师 ⭐⭐个人介绍&#xff1a;自己非常喜欢研究技术问题&#xff01;专业做Java、Python、微信小程序、安卓、大数据、爬虫、Golang、大屏等实战项目。 ⛽⛽实战项目&#xff1a;有源码或者技术上的问题欢迎在评论区一起讨论交流&#xff01; ⚡⚡ Java、…

软件测试实战,Web项目网页bug定位详细分析总结(详全)

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 1、前置条件 1&a…

9、Linux-安装JDK、Tomcat和MySql

目录 一、安装JDK 1、传输JDK文件&#xff08;.tar.gz&#xff09; 2、解压 3、备份环境变量 4、配置环境变量 5、重新加载环境变量 6、验证&#xff08;java -version&#xff09; 二、安装Tomcat 1、传输文件&#xff0c;解压到/usr/local 2、进入Tomcat的bin目录 …

数据库-ER图教程

一.什么是E-R图 E-R图全称&#xff1a;“Entity-Relationship Approach”&#xff0c;是一种“实体-联系”方法。 E-R图的优点&#xff1a; 1.自然地描述现实世界。 2.图形结构简单。 3.设计者和用户易理解。 4.是数据库设计的中间步骤&#xff0c;易于向数据模型转换。 …

44、网络编程/数据库相关操作练习20240306

一、代码实现数据库的创建&#xff08;员工信息表&#xff09;&#xff0c;并存储员工信息&#xff08;工号、姓名、薪资&#xff09;&#xff0c;能实现增加人员信息、删除人员信息、修改人员薪资操作。 代码&#xff1a; #include<myhead.h>int do_update(sqlite3 *p…

作业1-32 P1059 [NOIP2006 普及组] 明明的随机数

题目 思路 根据题意&#xff0c;需要将读入的数据排序&#xff0c;去重。 参考代码 #include<bits/stdc.h> using namespace std; int n,a[5000],k;int main() {while(cin>>n){//读入数据for(int i0;i<n;i)cin>>a[i];sort(a,an);//排序int b[5000];in…

chrome浏览器离线安装及历史版本的下载

背景&#xff1a;测试web功能在浏览器各版本的兼容性&#xff0c;需要用到旧版本的浏览器&#xff0c;当用户环境无法访问到互联网&#xff0c;需要下载离线版本安装&#xff1b; 1、在线版本安装 需要当前环境能正常使用互联网&#xff1a; 目前能访问的官网地址&#xff1…

【刷题】双指针入门

双指针入门 双指针283.移动零1089. 复写零202. 快乐数11. 盛最多水的容器Thanks♪(&#xff65;ω&#xff65;)&#xff89;谢谢阅读&#xff01;&#xff01;&#xff01;下一篇文章见&#xff01;&#xff01;&#xff01; 双指针 双指针是非常经典的算法&#xff0c;包括但…

6. Gin集成redis

文章目录 一&#xff1a;连接Redis二&#xff1a;基本使用三&#xff1a;字符串四&#xff1a;列表五&#xff1a;哈希六&#xff1a;Set七&#xff1a;管道八、事务九&#xff1a;示例 代码地址&#xff1a;https://gitee.com/lymgoforIT/golang-trick/tree/master/14-go-redi…