11.规则流

简介

规则流又称决策流,它整个的结构类似于工作流,用来对已有的决策集、决策表、交叉决策表、决策树、评分卡、复杂评分卡或其它决策流的执行顺序进行编排,以清晰直观的实现一个大的复杂的业务规则。

URule Pro规则引擎中的决策流可以实现对已有的决策集、决策表、交叉决策表、决策树、评分卡、复杂评分卡或其它决策流进行编排执行;编排过程中即可以常见串行执行,也可以并行执行、或者是根据条件选择分支执行。URule Pro中提供了一个基于网页的流程设计器,通过简单拖曳就可以快速实现对已有的决策集、决策表、交叉决策表、决策树、评分卡、复杂评分卡或其它决策流执行顺序的编排。

URule Pro的规则流设计器基于FlowDesigner项目实现,该项目在Github上的地址为:https://github.com/jacky6024/flowdesigner,flowdesigner是一款上海锐道自主研发的在浏览器中绘制流程图的Javascript库,利用它可以快速开发出流程图相关的设计器。

一个设计好的规则流如下图所示:

在这个流程设计器当中,上面是工具栏,下面是设计区,在工具栏第一排可实现流程模版的保存、选择、创建连接、重做、取消、网格吸附、删除、竖直居中对齐、水平居中对齐及将多个选中节点设置成相同尺寸等工具。

工具栏的第二排就是URule Pro中规则流支持的流程节点,URule Pro中规则流中共有八种类型的节点,分别是开始节点、规则节点、规则包节点、动作节点、脚本节点、决策节点、分支节点、聚合节点。

需要注意的是,URule Pro的规则流中没有结束节点,在URule Pro的规则流当中,规则流必须要以开始节点开始,可以在任何分支以任意节点结束,这点与类似UFLO 之类的工作流引擎不同,UFLO 之类的工作流引擎要求流必须要以开始节点开始,同时任何分支都必须要以结束节点结束。

创建决策流

打开URule Pro规则引擎控制台,在项目的“决策流”节点点右键,从右键菜单中选择创建决策流,创建一个新的决策流文件,如下图:

在设计器的设计区中,属性面板是可移动的,我们可以通过鼠标点击属性面板任何部位来移动它。点击工具栏第二行上的流程节点图标,然后在设计区单击,就可以在设计区添加对应节点。

在URule Pro的决策流中,节点图标的尺寸是可以通过鼠标改变的;节点创建完成后,可点击工具栏第一行上的连线图标,在节点间添加连接。点击工具栏上的选择图标,可实现节点或连线的选择,选择方式可以是点选,或拖选。

在定义好节点间的连线后,如需将连线变成折线,那么可以先采用拖选方式选中目标连线,如下图所示:

选中连线后,中连线中间就会出现可拖拽的锚点,拖动描点即可改变连线形状,如下图所示:

如不需要这个锚点,那么可以先取消连线的选择,然后再次选中连线,双击要删除的锚点,这样即可删除锚点对象,对应的连线也会回到没有锚点的状态。

节点或连接选中后就可以在属性面板上修改它们的属性,点击选择图标后,在设计区空白处点击就可以配置决策流的全局属性,如定义决策流ID,需要导入的库文件等。

决策流的全局属性有两块,第一块就是决策流的ID,这个很重要,在一知识包中,如果有多个决策流,那么决策流ID要唯一;第二部分是导入相关库文件,这与之前介绍的决策集、决策表、决策树、评分卡一样,唯一不同是这里的库文件管理放在了属性面板上。

接下来就来介绍URule Pro中决策流提供的各种类型节点的作用及使用方法。

开始节点

开始节点,是一个规则流开始的地方,在URule Pro当中,决策流必须要以开始节点开始,开始节点的属性比较简单,只有两个,如下表所示:

属性名 数据类型 描述
节点名称 String 设置当前节点名称
事件Bean String 一实现了com.bstek.urule.model.flow.NodeEvent接口配置在Spring中bean的id,一旦配置在流程进入及离开该节点时会触发这个实现类

NodeEvent接口源码如下:

package com.bstek.urule.model.flow;
import com.bstek.urule.model.flow.ins.FlowContext;
import com.bstek.urule.model.flow.ins.FlowInstance;
/**
 * @author Jacky.gao
 * @since 2015年4月20日
 */
public interface NodeEvent {
    /**
     * 规则流流入当前节点触发的方法
     * @param node 当前节点对象
     * @param instance 当前规则流实例对象
     * @param context 规则流上下文件对象
     */
    void enter(FlowNode node,FlowInstance instance,FlowContext context);
    /**
     * 规则流流出当前节点触发的方法
     * @param node 当前节点对象
     * @param instance 当前规则流实例对象
     * @param context 规则流上下文件对象
     */
    void leave(FlowNode node,FlowInstance instance,FlowContext context);
}

开始节点出入连接线如下表所示:

流入的连接线数量 流出的连接线数量
0 1

规则节点

所谓规则节点,用来绑定URule Pro当中提供的决策集、决策表、交叉决策表、决策树、评分卡、复杂评分卡或其它决策流文件的节点。值得注意的是,一个规则节点只能与一个当前项目中决策集、决策表、交叉决策表、决策树、评分卡、复杂评分卡或其它决策流文件绑定,这样一旦决策流流转到当前节点,那么就可以执行与这个规则节点绑定的决策集、决策表、交叉决策表、决策树、评分卡、复杂评分卡或其它决策流文件。

在设计器中,选中目标规则节点,就可以在属性面板中设置其相关属性,规则节点属性如下:

属性名 数据类型 描述
节点名称 String 设置当前节点名称
事件bean String 一实现了com.bstek.urule.model.flow.NodeEvent接口配置在Spring中bean的id,一旦配置在流程进入及离开该节点时会触发这个实现类
文件 String 与当前节点绑定的决策集、决策表、交叉决策表、决策树、评分卡、复杂评分卡或其它决策流文件
版本 String 与当前节点绑定的决策集、决策表、决策树、评分卡或其它决策流文件的版本

规则节点出入连接下如下表所示:

流入的连接线数量 流出的连接线数量
1~n 0~1

知识包节点

与规则节点不同,知识包节点是用来与具体的知识包绑定的,这样就可以实现复杂规则调用。知识包节点与某个知识包绑定之后,运行时规则流流转到这个节点后,就会执行与之绑定的知识包,如果绑定的知识包中包含决策流,那么引擎会自动执行其中的决策流,如果规则包中包含的规则流有多个,那么默认只会执行其中的第一个规则流,否则只执行触发规则动作。

知识包节点属性如下表所示:

属性名称 数据类型 描述
节点名称 String 设置当前节点名称
事件bean String 一实现了com.bstek.urule.model.flow.NodeEvent接口配置在Spring中bean的id,一旦配置在流程进入及离开该节点时会触发这个实现类
知识包 String 要与当前节点绑定的具体的知识包,我们可以通过下拉列表选择当前项目下已创建好的可用知识包。

知识包节点出入连接线如下表所示:

流入的连接线数量 流出的连接线数量
1~n 0~1

动作节点

动作节点可以与一个实现了com.bstek.urule.model.flow.FlowAction接口并配置到Spring中的Bean绑定,这样在运行时,规则流执行到这个动作节点时就会执行与之绑定的FlowAction实现类,动作节点属性如下表所示:

属性名称 数据类型 描述
节点名称 String 设置当前节点名称
事件bean String 一实现了com.bstek.urule.model.flow.NodeEvent接口配置在Spring中bean的id,一旦配置在流程进入及离开该节点时会触发这个实现类
动作bean String 一个实现了com.bstek.urule.model.flow.FlowAction接口并配置到Spring中的Bean的ID。

FlowAction接口源码如下所示:

package com.bstek.urule.model.flow;
import com.bstek.urule.model.flow.ins.FlowContext;
import com.bstek.urule.model.flow.ins.FlowInstance;
/**
 * @author Jacky.gao
 * @since 2015年2月28日
 */
public interface FlowAction {
    /**
     * @param node 当前节点对象
     * @param context 规则流上下文件对象
     * @param instance 当前规则流实例对象
     */
    void execute(ActionNode node,FlowContext context,FlowInstance instance);
}

有了动作节点,那么在规则流中就可以执行具体的Java类中的方法,因为该Java类是配置在Spring上下文中的,所以类中可访问Spring环境所有信息,这样就可以做一些更为复杂的业务操作。

动作节点出入连接线如下表所示:

流入的连接线数量 流出的连接线数量
1~n 0~1

脚本节点

顾名思义,脚本节点就是可以在这个节点上绑定一段脚本,这样在运行时,规则流流转到该节点时就可以执行这段脚本。脚本节点上的脚本属性就是我们编写要执行的脚本的地方,如下图所示:

在脚本属性中,我们提供了一个脚本编辑器,通过这个编辑器,结合代码提示(快捷键ALT+/)可快速编写要执行的脚本。脚本节点中编写的规则,完全遵循脚本式决策集中普通规则的then与end之间动作脚本编写语法规范。也就是说,脚本节点中添加的脚本没有if、then、end及条件判断脚本,有的只是执行动作的脚本。

在URule Pro中,脚本节点中直接写脚本已经不再推荐了,所以可以看到类型属性里有两个,一个是动作脚本,另一个是向导式动作。在动作脚本项里加上了(不推荐)的标注,而推荐的做法是选择向导式动作,选择向导式动作后就可以像在向导式规则集中那样,通过鼠标点击来完成动作的定义,如下图所示:

之所以不推荐使用脚本方式来定义动作,和之前在脚本式规则集里介绍原因一致,那么手写脚本易出错,向导方式简单,出错机率低,所以推荐大家使用。在后续版本中,脚本节点可能会删除脚本定义方式,只保留向导式动作定义方式。

脚本节点出入连接线如下表所示:

流入的连接线数量 流出的连接线数量
1~n 0~1

决策节点

所谓决策节点就是指在运行时,根据为其下流出连接配置的条件来决定究竟应该走哪条连接的节点,所以根据这一特性,决策节点下流出连接至少要有两条,否则决策节点就没有意义了。

选中决策节点,在其右边属性面板中就可以看到针对决策节点的配置,如下图所示:

需要重点介绍的是“决策类型”属性,决策节点的有两种决策类型,分别是"条件"和"百分比"。

当选择决策类型为条件时,就会看到如上图所示效果。选择“条件”类型时,在下面出现的在决策项当中,可以根据当前决策节点下流出连接的数量添加对应的决策项,对于每条决策项,都有两个属性,分别是"条件脚本"和“流向”,在条件列当中,我们可以编写具体的条件,在流向列中选择当条件列中定义的条件满足时要流出连线名称,所以对于决策节点下流出的连线,我们必须要为其设置名称,否则就无法为其定义决策项。

为连线定义名称,需要首先用拖选的方式选中它,然后就可以在属性面板上为其定义名称。

在条件表格中,点击条件编辑按钮,就会弹出条件编辑窗口,在这个窗口里,定义条件的方式有两种:一种是脚本方式;一种是向导方式。

脚本方式定义条件,其语法遵循脚本式规则中条件部分的语法规范,同样因为脚本需要手工编写的特性,所以现在同样也不再推荐使用脚本方式定义条件,而是推荐使用向导方式定义条件,如下图所示:

如果将决策类型改为“百分比”,则可以看到如下图所示效果:

如上图所示,一旦将决策类型改为“百分比”,那么就可以为每个流向设置流量百分比,如上图当中,在实际规则流运行时,将有30%走"c1"连线,70%走"c0"连线。配置时无论决策节点下有多少离开连线,最终所有的百分比加在一起要达到100%, 百分比这里一定要是一个合法的整数,否则会出现错误。

百分比模式下还有一个名为“作用范围”的选项,默认值为“批处理”,表示“百分比”类型的计算有效期为当前线程,一旦有新线程开启,那么这个百分比的值将开始重新计算;如果将“作用范围”改为“每次调用”,那么生效就不再局限于当前线程,每次调用在经过这个决策节点时都会根据百分比进行分流计算。

值得注意的时,当选择决策类型为“条件”,在运行时,当决策流流转到当前节点时,如果决策项中定义的各个条件都不满足,那么规则流到此就结束了,相反,如果有多个决策项满足时,那么系统将取第一条满足条件的决策项对应的流向连线进行向下流转,而不会选择所有满足条件的连线向下流转。

需要注意的是,百分比类型的“批处理”作用范围下决策方式,必须要在代码中通过批处理的方式执行才会生效,必须要使用后面章节里介绍的BatchSession来一次性处理一批数据,或者一个KnowledgeSession一次性处理一批数据,否则规则流永远都只会走默认的百分比占比最高的那条路径。 当然如果百分比类型的作用范围改为“每次调用”,那么规则的每次调用都会根据百分比来计算分流。

决策节点出入连接线下如下表所示:

流入的连接线 流出的连接线
1~n 1~n

分支节点

分支节点是URule Pro当中提供的一种可实现规则流多条并行的节点,通过这个节点,可以根据当前节点下流出连线数量,将当前规则流实现拆分成若干条子的规则流实例并行运行,根据这一特性,分支节点下至少要有两条流出的连线才有意义。

在决策流实例流转到分支节点时,分支节点会根据其下流出的连接线数量将主的实例拆分成与连线对应的若干个子实例,以并行方式继续运行产生的多个流实例。

分支节点出入连接线如下表所示:

流入的连接线数量 流出的连接线数量
1~n 1~n

默认情况下,分支节点会将主流程拆分成若干子流程执行,在实际执行的时候还是在一个线程内先后执行各个分支。

从2.2.1版本开始,在分支节点上新增一名为“启用多线程”的属性。默认情况下,如果不设置该属性,那么它的值为“系统默认”,这时将采用系统中定义的名为urule.flowForkMultiThread参数的值,由这个参数值来决定当前分支下是否采用多线程运行, urule.flowForkMultiThread参数值默认为false,也就是不开启多线程运行。

如果希望其下所有子分支以多线程形式并行,那么可以选中当前分支节点,将其“启用多线程”属性设置为“是”,或者将urule.flowForkMultiThread参数设置值为true, 这样引擎在执行到分支节点时会在不同的线程中执行其下各个分支,这对于各个分支业务逻辑不相关的业务,同时各个分支执行比较耗时,通过这样的配置让分支在不同线程里执行,所以可以明显提高系统执行性能。

需要注意的是分支节点的“启用多线程”属性值为“系统默认”时采用的是名为urule.flowForkMultiThread参数的值,否则就会覆盖这个全局参数的值,由当前分支节点自主决定是否开启多线程。

注意,当设置了参数urule.flowForkMultiThread=true 或配置了分支节点的“启用多线程”属性值为“是”时,分支节点下必须要添加一个聚合节点,将所有分支连接到聚合节点上,否则执行会出现错误。

聚合节点

聚合节点就是用来聚合由分支节点拆分出来的多个子的规则流的,所以有聚合节点,就一定要有分支节点,但有分支节点却不一定需要聚合节点(但如果设置了参数urule.flowForkMultiThread=true,,或配置了分支节点的“启用多线程”属性值为“是”时, 分支节点就启用了多线程执行功能,这样分支节点下一定需要一个聚合节点,否则会出现错误),对于URule Pro的决策流来说,拆分出子的决策流后是否有聚合节点是可选的,但聚合节点的出现则一定要有分支节点来配合,否则聚合节点就没有意义了。

聚合节点出入连接线如下表所示:

流入的连接线数量 流出的连接线数量
1~n 0~n

异常捕获节点

异常捕获节点的作用是捕获规则流中其它节点在执行时可能产生的异常,该节点从3.0.0版本开始添加,默认情况下在不添加异常捕获节点时,如果某个节点在运行时出现了异常,那么这个异常就会直接抛出,从而导致执行中断,为某个规则节点添加异常捕获节点后,一旦这个节点执行时出现异常, 那么异常会流向其下的异常捕获节点,我们可以在这个异常捕获节点中处理相应异常,也可以将规则流引流到其它的节点之后,从而避免由于异常而导致的执行中断,如下图所示:

)

在上图当中我们为“脚本1”节点添加了一个异常捕获节点,这样,在运行时,一旦“脚本1”节点产生异常,那么规则流不会流转到“脚本2”节点,而是流转到“异常捕获1”节点,然后再流转到“脚本11”节点。 运行时我们会发现,如果“脚本1”节点正常运行,那么规则流会正常流转到“脚本2”节点,一旦发生异常,规则流就会流向其下的异常捕获节点,从而改变了规则流向,同时规则计算也不会中断。

异常捕获节点有两个属性需要我们注意:

属性名 含义
要捕获的Exception完成类名 默认它的值是java.lang.Exception,也就是说所有的异常都会被捕获,当然我可以修改这个属性,以使得当前节点只会捕获某个特定的Exception,需要注意的是我们需要输入完整的类名(包含包名),且必须是java.lang.Exception类或子类,该属性不能为空
处理Exception的Bean 一个实现了com.bstek.urule.model.flow.ExceptionHandler接口并配置到Spring中的Bean的ID,默认值为urule.defaultExceptionHandler,引擎提供的一个会向控制台输出异常堆栈的默认实现,当然我们可以根据需要自己实现一个,该属性可以为空

示例

下图中是一个简单的决策流定义,它由一个开始节点、三个脚本节点和一个决策节点构成。

在这个决策流中,我们定义它的ID为“flow-demo”,同时导入了我们之前定义好的包含“会员”的变量库文件。决策流在经过“开始”节点后,进入“脚本1”节点,这个节点上定义的脚本比较简单,只是简单向控制台输出一段文本,如下图:

接下来就进入“决策”节点,在决策节点,我们选择的是“条件”作为决策类型,分别定义了两条连线的流向条件,如下图:

流向“to2”连线上定义的条件内容如下图:

流向“to3”连线上定义的条件内容如下图:

从上面的两张图中可以看到,当会员对象的等级属性在0~5之间时选择"to2"连线;在5~10之间时选择"to3"连线。

决策节点下面是两个脚本节点,为他们定义脚本内容如下图:

决策流定义的信息就是这些,接下来我们在知识包节点对其进行仿真测试,看看运行路径是不是如我们所预期。

打开“知识包”节点,在其下创建一个新的知识包,将这个决策流文件添加到知识包中,点击工具栏上的“仿真测试”按钮,在弹出的窗口中输入“会员”的等级属性值,点击工具栏上的“测试决策流”按钮,在弹出的窗口中选择我们定义的“flow-demo”,点击“操作列”上的测试图标,既完成了对当前决策流的测试,如下图:

查看控制台,可以看到如下图所示的内容输出:

在上面的例子中,脚本节点里的动作以及决策节点中的条件都是采用脚本的方式定义,这里您也可以改成向导方式定义,比较一下两种定义方式的区别,这里就不再赘述。

results matching ""

    No results matching ""