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

本文主要是介绍SpringBoot中集成LiteFlow(轻量、快速、稳定可编排的组件式规则引擎)实现复杂业务解耦、动态编排、高可扩展,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

场景

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

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

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

一些拥有复杂业务逻辑的系统,核心业务逻辑冗长,涉及内部逻辑运算,缓存操作,持久化操作,外部资源调取,内部其他系统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 {@Overridepublic void process() {//do your businessSystem.out.println("acmp执行");}
}

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

import com.yomahub.liteflow.core.NodeComponent;
import org.springframework.stereotype.Component;@Component("bcmp")
public class BCmp extends NodeComponent {@Overridepublic void process() {//do your businessSystem.out.println("bcmp执行");}
}
import com.yomahub.liteflow.core.NodeComponent;
import org.springframework.stereotype.Component;@Component("ccmp")
public class CCmp extends NodeComponent {@Overridepublic void process() {//do your businessSystem.out.println("ccmp执行");}
}

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

🌿Springboot下的配置项 | LiteFlow

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

编写单元测试

    @Testpublic 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 {@Overridepublic 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 {@Overridepublic 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案例

滑动验证页面

这篇关于SpringBoot中集成LiteFlow(轻量、快速、稳定可编排的组件式规则引擎)实现复杂业务解耦、动态编排、高可扩展的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/782190

相关文章

C++使用栈实现括号匹配的代码详解

《C++使用栈实现括号匹配的代码详解》在编程中,括号匹配是一个常见问题,尤其是在处理数学表达式、编译器解析等任务时,栈是一种非常适合处理此类问题的数据结构,能够精确地管理括号的匹配问题,本文将通过C+... 目录引言问题描述代码讲解代码解析栈的状态表示测试总结引言在编程中,括号匹配是一个常见问题,尤其是在

Java实现检查多个时间段是否有重合

《Java实现检查多个时间段是否有重合》这篇文章主要为大家详细介绍了如何使用Java实现检查多个时间段是否有重合,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录流程概述步骤详解China编程步骤1:定义时间段类步骤2:添加时间段步骤3:检查时间段是否有重合步骤4:输出结果示例代码结语作

Java中String字符串使用避坑指南

《Java中String字符串使用避坑指南》Java中的String字符串是我们日常编程中用得最多的类之一,看似简单的String使用,却隐藏着不少“坑”,如果不注意,可能会导致性能问题、意外的错误容... 目录8个避坑点如下:1. 字符串的不可变性:每次修改都创建新对象2. 使用 == 比较字符串,陷阱满

Java判断多个时间段是否重合的方法小结

《Java判断多个时间段是否重合的方法小结》这篇文章主要为大家详细介绍了Java中判断多个时间段是否重合的方法,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录判断多个时间段是否有间隔判断时间段集合是否与某时间段重合判断多个时间段是否有间隔实体类内容public class D

使用C++实现链表元素的反转

《使用C++实现链表元素的反转》反转链表是链表操作中一个经典的问题,也是面试中常见的考题,本文将从思路到实现一步步地讲解如何实现链表的反转,帮助初学者理解这一操作,我们将使用C++代码演示具体实现,同... 目录问题定义思路分析代码实现带头节点的链表代码讲解其他实现方式时间和空间复杂度分析总结问题定义给定

IDEA编译报错“java: 常量字符串过长”的原因及解决方法

《IDEA编译报错“java:常量字符串过长”的原因及解决方法》今天在开发过程中,由于尝试将一个文件的Base64字符串设置为常量,结果导致IDEA编译的时候出现了如下报错java:常量字符串过长,... 目录一、问题描述二、问题原因2.1 理论角度2.2 源码角度三、解决方案解决方案①:StringBui

Java覆盖第三方jar包中的某一个类的实现方法

《Java覆盖第三方jar包中的某一个类的实现方法》在我们日常的开发中,经常需要使用第三方的jar包,有时候我们会发现第三方的jar包中的某一个类有问题,或者我们需要定制化修改其中的逻辑,那么应该如何... 目录一、需求描述二、示例描述三、操作步骤四、验证结果五、实现原理一、需求描述需求描述如下:需要在

Debezium 与 Apache Kafka 的集成方式步骤详解

《Debezium与ApacheKafka的集成方式步骤详解》本文详细介绍了如何将Debezium与ApacheKafka集成,包括集成概述、步骤、注意事项等,通过KafkaConnect,D... 目录一、集成概述二、集成步骤1. 准备 Kafka 环境2. 配置 Kafka Connect3. 安装 D

Java中ArrayList和LinkedList有什么区别举例详解

《Java中ArrayList和LinkedList有什么区别举例详解》:本文主要介绍Java中ArrayList和LinkedList区别的相关资料,包括数据结构特性、核心操作性能、内存与GC影... 目录一、底层数据结构二、核心操作性能对比三、内存与 GC 影响四、扩容机制五、线程安全与并发方案六、工程

JavaScript中的reduce方法执行过程、使用场景及进阶用法

《JavaScript中的reduce方法执行过程、使用场景及进阶用法》:本文主要介绍JavaScript中的reduce方法执行过程、使用场景及进阶用法的相关资料,reduce是JavaScri... 目录1. 什么是reduce2. reduce语法2.1 语法2.2 参数说明3. reduce执行过程