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

猜你喜欢

转载自wangwei86609.iteye.com/blog/2382490