Osmanthus 规则引擎1.0公布了
Osmanthus 规则引擎1.0发布了
Osmanthus 是什么?
Osmanthus 是一款JAVA实现的轻量级开源的规则引擎框架,它是基于MVEL组件实现的,相比drools使用更容易且更轻量级;支持复杂的规则集,例如:决策树,评分卡等;配置规则可以像配置流程一样;支持规则的并行的执行。
源码地址:https://github.com/wangwei86609/osmanthus
另注:该框架还在持续更新中,欢迎广大编程爱好者积极参与该框架的开发和维护。
Osmanthus 目标
取代drools,实现一款更加强大的规则引擎框架。
核心特征
轻量级JAVA实现,且宜用
基于XML文件规则配置,规则更容易维护,且后期升级空间会更大
支持MVEL表达式语言,使规则中Condition 和 Action配置更方便,不需要硬编码
对规则做了很好的抽象,实现复杂规则例如:决策树和决策树林,评分卡等等
可以并行执行规则
Osmanthus使用
让我们实现如下流程规则集,该规则集相当复杂,包含了:决策树,评分看,并行规则,并行规则合并:
对于以上如此复杂的规则,在Osmanthus中是很容易实现的,定义如下xml的规则的配置文件,详见源码
<?xml version="1.0" encoding="UTF-8"?> <flow id="flow1"> <start id="start" toNodeId="feerule"/> <ruleset id="feerule" fromNodeId="start" toNodeId="split1" external="true"/> <split id="split1" fromNodeId="feerule"> <constraint toNodeId="card"> <condition><![CDATA[fee>1]]></condition> </constraint> <constraint toNodeId="end"> <condition><![CDATA[fee<=1]]></condition> </constraint> </split> <ruleset id="card" fromNodeId="split1" toNodeId="mlines" external="true"/> <parallel id="mlines" fromNodeId="card"> <line toNodeId="p1"/> <line toNodeId="p2"/> </parallel> <rule id="p1" fromNodeId="mlines" toNodeId="end"> <condition><![CDATA[1==1]]></condition> <action><![CDATA[rule1="p1"]]></action> </rule> <rule id="p2" fromNodeId="mlines" toNodeId="mlines2"> <condition><![CDATA[2==2]]></condition> <action><![CDATA[rule2="p2"]]></action> </rule> <parallel id="mlines2" fromNodeId="p2"> <line toNodeId="p3"/> <line toNodeId="p4"/> </parallel> <rule id="p3" fromNodeId="mlines2" toNodeId="merge"> <condition><![CDATA[1==1]]></condition> <action><![CDATA[rule3="p3"]]></action> </rule> <rule id="p4" fromNodeId="mlines2" toNodeId="merge"> <condition><![CDATA[2==2]]></condition> <action><![CDATA[rule4="p4"]]></action> </rule> <merge id="merge" fromNodeId="p3,p4" lineCnt="2" toNodeId="p5"/> <rule id="p5" fromNodeId="merge" toNodeId="end"> <condition><![CDATA[2==2]]></condition> <action><![CDATA[rule5="p5"]]></action> </rule> <end id="end"/> </flow>
执行规则代码:
package org.wei86609.osmanthus; import junit.framework.TestCase; import org.wei86609.osmanthus.event.Event; public class OsmanthusExecutorTest extends TestCase { public void testExecute() { Event event=new Event(); event.setEventId("flow1"); event.add("salary", 5000); event.add("weight", 500); event.add("isBlackName", true); event.add("fee", 500); event.add("name", "test"); event.add("reg", "12312"); try { OsmanthusExecutor executor= new OsmanthusExecutor(); executor.newEvent(event, null); Thread.sleep(5000); } catch (Exception e) { e.printStackTrace(); } } }
输出结果:
2017-07-02 23:12:47,644-DEBUG [pool-1-thread-1]->(FlowEngine.java:37) Osmanthus start to execute the event[Event [eventId=flow1, threadId=pool-1-thread-1, model=FIRST, parameters={fee=500, isBlackName=true, weight=500, reg=12312, name=test, salary=5000}]] 2017-07-02 23:12:47,896-DEBUG [pool-1-thread-1]->(FlowEngine.java:41) Node is blank, will get the first node [start] of flow to execute. 2017-07-02 23:12:47,896-DEBUG [pool-1-thread-1]->(EmptyNodeExecutor.java:13) The empty node[start] of the event{Event [eventId=flow1, threadId=pool-1-thread-1, model=FIRST, parameters={fee=500, isBlackName=true, weight=500, reg=12312, name=test, salary=5000}]} executed. 2017-07-02 23:12:47,899-DEBUG [pool-1-thread-1]->(GeneralRuleSetExecutor.java:33) The ruleset[feerule] of the event{Event [eventId=flow1, threadId=pool-1-thread-1, model=FIRST, parameters={fee=500, isBlackName=true, weight=500, reg=12312, name=test, salary=5000}]} has [8] rules 2017-07-02 23:12:47,976-DEBUG [pool-1-thread-1]->(RuleExecutor.java:22) The node[step2] of the event {flow1} condition=[salary>3500 && salary<=5000] is true and action=[fee=(salary-3500)*0.03] 2017-07-02 23:12:47,977-DEBUG [pool-1-thread-1]->(GeneralRuleSetExecutor.java:64) The node[step2] of the event {Event [eventId=flow1, threadId=pool-1-thread-1, model=FIRST, parameters={fee=45.0, isBlackName=true, weight=500, reg=12312, name=test, salary=5000}]} is exclusive, remain nodes will not be executed. 2017-07-02 23:12:47,978-DEBUG [pool-1-thread-1]->(SplitRuleExecutor.java:18) Split[split1] of the event {Event [eventId=flow1, threadId=pool-1-thread-1, model=FIRST, parameters={fee=45.0, isBlackName=true, weight=500, reg=12312, name=test, salary=5000}]} has [2] Constraints 2017-07-02 23:12:47,978-DEBUG [pool-1-thread-1]->(SplitRuleExecutor.java:21) Split[split1] of the event {Event [eventId=flow1, threadId=pool-1-thread-1, model=FIRST, parameters={fee=45.0, isBlackName=true, weight=500, reg=12312, name=test, salary=5000}]} Constraint's condition [fee>1] result is true, will link to Node[card]. 2017-07-02 23:12:47,978-DEBUG [pool-1-thread-1]->(GeneralRuleSetExecutor.java:33) The ruleset[card] of the event{Event [eventId=flow1, threadId=pool-1-thread-1, model=FIRST, parameters={fee=45.0, isBlackName=true, weight=500, reg=12312, name=test, salary=5000}]} has [4] rules 2017-07-02 23:12:47,979-DEBUG [pool-1-thread-1]->(RuleExecutor.java:22) The node[gc2] of the event {flow1} condition=[isBlackName] is true and action=[weight=weight-200] 2017-07-02 23:12:47,980-DEBUG [pool-1-thread-1]->(RuleExecutor.java:22) The node[gc3] of the event {flow1} condition=[name=='test'] is true and action=[weight=weight-500] 2017-07-02 23:12:47,982-DEBUG [pool-1-thread-1]->(RuleExecutor.java:22) The node[gc4] of the event {flow1} condition=[reg ~= "[1-9][0-9]{4,}"] is true and action=[weight=weight-600] 2017-07-02 23:12:47,983-DEBUG [pool-1-thread-1]->(ParallelRuleExecutor.java:21) Parallel[mlines] of the event {flow1} has [2] thread lines 2017-07-02 23:12:47,984-DEBUG [pool-1-thread-1]->(FlowEngine.java:44) Osmanthus execute the event {Event [eventId=flow1, threadId=pool-1-thread-1, model=FIRST, parameters={fee=45.0, isBlackName=true, weight=-800, reg=12312, name=test, salary=5000}]} end 2017-07-02 23:12:47,984-DEBUG [pool-1-thread-2]->(FlowEngine.java:37) Osmanthus start to execute the event[Event [eventId=flow1, threadId=pool-1-thread-2, model=FIRST, parameters={isBlackName=true, fee=45.0, weight=-800, reg=12312, name=test, salary=5000}]] 2017-07-02 23:12:47,985-DEBUG [pool-1-thread-3]->(FlowEngine.java:37) Osmanthus start to execute the event[Event [eventId=flow1, threadId=pool-1-thread-3, model=FIRST, parameters={isBlackName=true, fee=45.0, weight=-800, reg=12312, name=test, salary=5000}]] 2017-07-02 23:12:48,032-DEBUG [pool-1-thread-2]->(RuleExecutor.java:22) The node[p1] of the event {flow1} condition=[1==1] is true and action=[rule1="p1"] 2017-07-02 23:12:48,033-DEBUG [pool-1-thread-2]->(EmptyNodeExecutor.java:13) The empty node[end] of the event{Event [eventId=flow1, threadId=pool-1-thread-2, model=FIRST, parameters={isBlackName=true, fee=45.0, weight=-800, reg=12312, name=test, rule1=p1, salary=5000}]} executed. 2017-07-02 23:12:48,033-DEBUG [pool-1-thread-2]->(FlowEngine.java:44) Osmanthus execute the event {Event [eventId=flow1, threadId=pool-1-thread-2, model=FIRST, parameters={isBlackName=true, fee=45.0, weight=-800, reg=12312, name=test, rule1=p1, salary=5000}]} end 2017-07-02 23:12:48,070-DEBUG [pool-1-thread-3]->(RuleExecutor.java:22) The node[p2] of the event {flow1} condition=[2==2] is true and action=[rule2="p2"] 2017-07-02 23:12:48,070-DEBUG [pool-1-thread-3]->(ParallelRuleExecutor.java:21) Parallel[mlines2] of the event {flow1} has [2] thread lines 2017-07-02 23:12:48,070-DEBUG [pool-1-thread-2]->(FlowEngine.java:37) Osmanthus start to execute the event[Event [eventId=flow1, threadId=pool-1-thread-2, model=FIRST, parameters={fee=45.0, isBlackName=true, weight=-800, reg=12312, name=test, salary=5000, rule2=p2}]] 2017-07-02 23:12:48,070-DEBUG [pool-1-thread-3]->(FlowEngine.java:44) Osmanthus execute the event {Event [eventId=flow1, threadId=pool-1-thread-3, model=FIRST, parameters={isBlackName=true, fee=45.0, weight=-800, reg=12312, name=test, rule2=p2, salary=5000}]} end 2017-07-02 23:12:48,070-DEBUG [pool-1-thread-1]->(FlowEngine.java:37) Osmanthus start to execute the event[Event [eventId=flow1, threadId=pool-1-thread-1, model=FIRST, parameters={fee=45.0, isBlackName=true, weight=-800, reg=12312, name=test, salary=5000, rule2=p2}]] 2017-07-02 23:12:48,115-DEBUG [pool-1-thread-2]->(RuleExecutor.java:22) The node[p3] of the event {flow1} condition=[1==1] is true and action=[rule3="p3"] 2017-07-02 23:12:48,116-DEBUG [pool-1-thread-2]->(MergeNodeExecutor.java:16) Merge[merge] of the event {Event [eventId=flow1, threadId=pool-1-thread-2, model=FIRST, parameters={fee=45.0, isBlackName=true, weight=-800, reg=12312, name=test, rule3=p3, salary=5000, rule2=p2}]} has [0] threads need to merge 2017-07-02 23:12:48,116-DEBUG [pool-1-thread-1]->(RuleExecutor.java:22) The node[p4] of the event {flow1} condition=[2==2] is true and action=[rule4="p4"] 2017-07-02 23:12:48,117-DEBUG [pool-1-thread-1]->(MergeNodeExecutor.java:16) Merge[merge] of the event {Event [eventId=flow1, threadId=pool-1-thread-1, model=FIRST, parameters={fee=45.0, isBlackName=true, weight=-800, rule4=p4, reg=12312, name=test, salary=5000, rule2=p2}]} has [0] threads need to merge 2017-07-02 23:12:48,117-DEBUG [pool-1-thread-2]->(MergeNodeExecutor.java:18) Merge[merge] of the event {Event [eventId=flow1, threadId=pool-1-thread-2, model=FIRST, parameters={fee=45.0, isBlackName=true, weight=-800, reg=12312, name=test, rule3=p3, salary=5000, rule2=p2}]} will merge all threads 2017-07-02 23:12:48,117-DEBUG [pool-1-thread-1]->(MergeNodeExecutor.java:18) Merge[merge] of the event {Event [eventId=flow1, threadId=pool-1-thread-1, model=FIRST, parameters={fee=45.0, isBlackName=true, weight=-800, rule4=p4, reg=12312, name=test, salary=5000, rule2=p2}]} will merge all threads 2017-07-02 23:12:48,117-DEBUG [pool-1-thread-2]->(RuleExecutor.java:22) The node[p5] of the event {flow1} condition=[2==2] is true and action=[rule5="p5"] 2017-07-02 23:12:48,117-DEBUG [pool-1-thread-1]->(RuleExecutor.java:22) The node[p5] of the event {flow1} condition=[2==2] is true and action=[rule5="p5"] 2017-07-02 23:12:48,118-DEBUG [pool-1-thread-2]->(EmptyNodeExecutor.java:13) The empty node[end] of the event{Event [eventId=flow1, threadId=pool-1-thread-2, model=FIRST, parameters={fee=45.0, isBlackName=true, weight=-800, rule5=p5, reg=12312, name=test, rule3=p3, salary=5000, rule2=p2}]} executed. 2017-07-02 23:12:48,118-DEBUG [pool-1-thread-1]->(EmptyNodeExecutor.java:13) The empty node[end] of the event{Event [eventId=flow1, threadId=pool-1-thread-1, model=FIRST, parameters={fee=45.0, isBlackName=true, weight=-800, rule5=p5, rule4=p4, reg=12312, name=test, salary=5000, rule2=p2}]} executed. 2017-07-02 23:12:48,118-DEBUG [pool-1-thread-2]->(FlowEngine.java:44) Osmanthus execute the event {Event [eventId=flow1, threadId=pool-1-thread-2, model=FIRST, parameters={fee=45.0, isBlackName=true, weight=-800, rule5=p5, reg=12312, name=test, rule3=p3, salary=5000, rule2=p2}]} end 2017-07-02 23:12:48,118-DEBUG [pool-1-thread-1]->(FlowEngine.java:44) Osmanthus execute the event {Event [eventId=flow1, threadId=pool-1-thread-1, model=FIRST, parameters={fee=45.0, isBlackName=true, weight=-800, rule5=p5, rule4=p4, reg=12312, name=test, salary=5000, rule2=p2}]} end