LiteFlow
是一个编排式的规则引擎框架,组件编排,帮助解耦业务代码,让每一个业务片段都是一个组件。
IDAE插件
LiteFlow
拥有自己的IDEA
插件LiteFlowX
。通过该插件能支持规则文件的智能提示、语法高亮、组件与规则文件之间的跳转及
LiteFlow
工具箱等功能。
规则表达式
串行编排:
当想要依次执行a、b、c、d四个组件时,直接使用
THEN
关键字即可。
<chain name="chain1">
THEN(a, b, c, d);
</chain>
并行编排:
如果想并行执行
a、b、c
三个组件的话,可以使用WHEN
关键字。
<chain name="chain1">
WHEN(a, b, c);
</chain>
选择编排:
如果想实现代码中的
switch
逻辑的话,例如通过a组件的返回结果进行判断。如果返回的是组件名称b的话则执行b组件,可以使用
SWITCH
关键字。
<chain name="chain1">
SWITCH(a).to(b, c, d);
</chain>
条件编排:
如果想实现代码中的
if逻辑
的话,例如当x组件
返回为true
时执行a
,可以使用IF
关键字。
<chain name="chain1">
IF(x, a);
</chain>
如果想实现if的
三元运算符
逻辑的话,例如x组件返回为true时执行a组件,返回为false时执行b组件。
<chain name="chain1">
IF(x, a, b);
</chain>
如果想实现
if else
逻辑的话,可以使用ELSE
关键字,和上面实现效果等价。
<chain name="chain1">
IF(x, a).ELSE(b);
</chain>
如果想实现
else if
逻辑的话,可以使用ELIF
关键字。
<chain name="chain1">
IF(x1, a).ELIF(x2, b).ELSE(c);
</chain>
使用子流程:
当某些流程比较复杂时,可以定义子流程,然后在主流程中引用,这样逻辑会比较清晰。
例如我们有如下子流程,执行C、D组件。
<chain name="subChain">
THEN(C, D);
</chain>
然后我们直接在主流程中引用子流程即可。
<chain name="mainChain">
THEN(
A, B,
subChain,
E
);
</chain>
相关组件
普通组件需要继承
NodeComponent
并实现process()
方法,还需设置@Component
注解的名称。
- 可以通过重写
isAccess
方法来决定是否执行该组件。
LiteFlow
的组件在规则文件中即对应的节点:
普通组件
普通组件需要集成的是
NodeComponent
, 可以用在when 和 then
逻辑中,具体的业务需要在process
中去执行。
同时在
node
节点中,可以覆盖isAccess
方法,表示是否进入该节点执行业务逻辑。
isContinueOnError
判断在出错的情况下是否继续执行下一个组件,默认为 false。
isEnd
方法表示是否终止流程,默认为true。选择组件
选择组件是通过业务逻辑来判断接下来的动作要执行哪一个节点,类似于
Java中的 switch
。在代码中则需要继承
NodeSwitchComponent
实现processWitch
方法来处理业务。条件组件
条件组件称之为
if
组件,返回的结果是 true 或者 false,代码需要集成NodeIfComponent
重写processIf
方法。
- 返回对应的业务节点,这个和选择组件类似。
数据上下文
LiteFlow
上下文对象起到参数传递的作用,因为不同业务需要的输入输出参数是不同的。上下文传入的是一个
class
类型参数,一般情况下是在第一个节点中,将传入参数设置到上下文对象中。
# 执行流程时,需要传递el文件,初始化参数以及上下文对象,这里的上下文可以设置多个
LiteflowResponse response = flowExecutor.execute2Resp("chain1", 流程初始参数, CustomContext.class);
业务实践
使用电商场景的应用,订单完成后,进行积分的发放,消息发送,同时并行发送短信和邮件。
<?xml version="1.0" encoding="UTF-8"?>
<flow>
<chain name="test_flow">
THEN(
prepareTrade, grantScore, sendMq, WHEN(sendEmail, sendPhone)
);
</chain>
</flow>
在订单完成之后异步执行,传递参数并执行相应的规则流程。
// 处理 交易完成后任务,异步执行
@Async(value = "getAsyncExecutor")
public void handleApp(AppFlowDto flowDto){
// 使用的规则文件,传递参数,上下文对象
LiteflowResponse response = flowExecutor.execute2Resp("test_flow", flowDto, AppFLowContext.class);
// 获取流程执行后的结果
if (!response.isSuccess()) {
Exception e = response.getCause();
Log.warn(" error is {}", e.getCause(),e);
}
AppFlowContext context = response.getContextBean(AppFlowContext.class);
log.info("handleApp 执行完成后 context {}",JSONObject.toJSONString(context));
}
在正式处理业务流程之前,需要先进行数据的预处理,将流程入参转转换成上下文对象,方便参数的传递和设置。
// 数据准备和校验处理
@Slf4j
@Component(valve = "prepareTrade")
public class PrepareTrade extends NodeComponent {
@Override
public void process() throws Exception {
log.info("交易完成后业务处理数据准备和校验");
// 拿到请求参数
AppFlowDto req = this.getslot().getRequestData();
log.info("请求参数 {}",JSONObject.toJSONString(req));
// 停止任务
// setIsEnd(Boolean.TRUE);
AppFlowContext context = this.getContextBean(AppFlowContext.class);
log.info("设置上下文对象{}",JSONObject.toJSONString(context));
}
在具体的业务处理环节,以积分发放为例,可以获取上下文对象进行业务操作。
同时也可以重写
isAccess
方法,来判断是否处理该节点。具体的业务流程都可以抽象成一个
node
节点,存放在test_flow.el.xml
中进行执行。
@Slf4j
@Component(value="grantScore")
public class GrantScore extends NodeComponent {
@Override
public void process() throws Exception {
AppFlowContext context = this.getContextBean(AppFlowContext.class);
log.info("business cxt {}",JSONObject.toJSONString(context));
TimeUnit.SECONDS.sleep(RandomUtil.randomInt(0,20));
}
// 是否处理该节点
@Override
public boolean isAccess() throws Exception {
AppFlowContext context = this.getContextBean(AppFlowContext.class);
log.info("判断是否处理该节点 cxt {}",JSONObject.toJSONString(context));
return Boolean.TRUE;
}
}