??xml version="1.0" encoding="utf-8" standalone="yes"?>
Q一Q什么时候应该用规则引?/strong>
q实际是一个技术选型的问题。但q个问题又似乎是一个很关键的问题(一旦返工的话,你就知道q个问题是多么重要了(jin)Q。不知大家有没有q这L(fng)l验和体?x)。往往在项目开始的时候,M(x)遇到应该选用什么技术?是不是应该用最新的技术?或者应该选用什么技术呢Q?em>PSQ现在计机软g中的各种技术层ZIPhcM功能的技术很?/em>Q?
不管怎么Pq些问题M(x)困扰着我。比如,q次的这个项目。项目要求是要在一些log文g中(q些log文g都是很大的应用系l所产生的,但由于legacy的原因,log本n的维护和规范工作一直没有得到改善,所以想借助于一些外部应用对q些log做以分析和清z?/em>Q抽取出有用的信息?br />
于是Q第一个想到的是Q这是一个文本挖掘类的项目。但又想Q要抽取有用信息Q必d建立一些规则或patternQ模式)(j)。所以,我第一个想C(jin)规则引擎。因里面要徏立好多规则,而这些规则可以独立于代码U别Q?em>攑ֈ一个单独的drl文g?/em>Qƈ可以用规则引擎去解析和执行。另一个重要的原因是,我原来用q,比较熟?zhn)。这P也可以节省开发时间吧。于是,好不犹U的就开始做?jin)Demo....
但事实上Q在l历?jin)一个多星期的编码、测试后Q我发现q用规则引擎实在是太W拙?jin)?br />
Q?Q首先必d立一些数据模型。通过q些模型来refer规则文g中的LHS和Action?br />
Q?Q还要考虑规则的conflict。如果有一些规则同时被触发Q就要考虑讑֮规则的优先或者是讑֮activiation-group来保证在一个group中的规则只有一个规则可以被触发?br />
Q?Q对?#8216;?#8217;规则group ruleflow-group的用。如果要控制在workingmemory中的规则被触发的序Q则可以这些规则分l。然后,通过规则建模的方式来实现。但q也d?jin)一定的effort。修Ҏ(gu)者更C大方ѝ?br />
所以,Z上述体会(x)Q我更认则引擎更适用于那些对非流E性规则匹配的应用。当?dng)Drools也支持对程性规则的建模q程。但Q这也许不是最好的方式?br />
Q二QDrools规则引擎的用杂?/strong>
Q?QFact 的变更监?/span>。在Drools里,如果一个Fact通过规则而改变,则需这U改变通知l规则引擎。这里,一般有两种方式Q显式和隐式?br />
昑ּ---在drl文g中通过 update、modify来通知Q在E序中,通过Fact的引用调用modifyObject{方法来实现?br />
隐式---通过在java bean实现property Listener Interface来让引擎自动监听到属性值的变化。我更习(fn)惯于q种方式。因为,一般看来凡是在规则引擎中添加到fact都是希望引擎来帮你进行管理的。所以,那它自己看到fact的变化是U很省事的办法。也很简单,是用java bean property 监听的方式?br />
通过StatefulSession来注册?br />
调用StatefulSession的某个instance 的insertQObjectQtrueQ实现。而这个object是一个java bean。其中,要实?strong>
private final PropertyChangeSupport changes = new PropertyChangeSupport( this );
public void addPropertyChangeListener(final PropertyChangeListener l) {
this.changes.addPropertyChangeListener( l );
}
public void removePropertyChangeListener(final PropertyChangeListener l) {
this.changes.removePropertyChangeListener( l );
}
然后在setҎ(gu)中调?br />
this.changes.firePropertyChange( "temp",null,this.temp );
Q?Q规则触发的优先U、组讄
往往Q在设计我们自己的规则时Q要考虑规则的触发条件。这不仅限于LHS的条仉分,q有规则本n被触发的有些讄{等。这里,列出一些比较常用和有效的规则优先讄方式Q以?qing)需要注意的地方?br />
A.通过Salience方式。此值可正可负。越大优先高Q也?x)被引擎首先执行?br />
B.通过ruleflow-group 方式。实际上Q用这U方式也是在用徏立规则流的方式。在Eclipse 3.3 中,Drools提供?jin)徏立规则流的插件。要在drl的同U目录中建立rf和rfm两个文gQ当?dng)插g?x)帮助你建立q些Q?br />
选择RuleFlow File?br />
q里Q需要注意的一Ҏ(gu)要在启动规则引擎的时候,加入启动rule flow的代码?br />
InputStreamReader source = new InputStreamReader(RuleManager.class
.getResourceAsStream(rule_path));
PackageBuilder builder = new PackageBuilder();
builder.addPackageFromDrl(source);
builder.addRuleFlow(new InputStreamReader(RuleManager.class
.getResourceAsStream(rule_flow_path)));
Package pkg = builder.getPackage();
RuleBase ruleBase = RuleBaseFactory.newRuleBase();
ruleBase.addPackage(pkg);
然后Q在每次启动规则引擎的时候,调用如下Ҏ(gu)Q?br />
StatefulSession ss;
ss.startProcess(flowProgress)Q?br />
ss.fireAllRules();
flowProgress 是一个stringcd。这个flow的名字?br />
q个rule flow图中Q显CZ(jin)一个简单的规则。如RSA是一个rule-flow的名字。在q个rule set中可以设定一lrules。这P可以分开规则执行的顺序。在于rf和rfm同名的另一?drl文g中定义这些组的名字。通过关键?ruleflow-group 来表明?br />
C.通过activation-group的方式。通过q种方式Q可以exclude一lrule中一旦有一个rule被invokeQ而其它r(ji)ule不会(x)被execute。同Ӟ可以搭配使用salience关键字来标明每个rule的优先Q这样就能够使得你想要的一般性概늚rule先被匚w执行?br />
D.在用ruleflow-group 的时候要注意使用lock-on-active true 关键字在每个rule。这样可以避免一旦有rule被触发,不会(x)造成循环匚w执行?br />
E.如果在LHS部分Q需要调用某个方法来q回真、假g为判断的一个条Ӟ那么可以用eval函数?br />
如,eval(pattern.matched(5,$line.getCurrLine()))
其中Qpattern是某个加入到workingmemory中的一个实例。matched是这个实例所代表cȝ一个方法。它q回booleancd?br />
(3)Drools规则引擎的用感?br />
MQDroolsq是一个很不错的开源规则引擎。现在v4.0以上的版本已l比以前的版本在速度上有?jin)很大的提升。可以作为我们一般应用程序系l的中间件品(那些规则不是很经常改变的pȝQ已l非程c规则)(j)。但是,q其中还是需要一些额外的effort来学?fn)它的用文档以及(qing)整体架构,有一定的学习(fn)曲线?br />
最后,我想一个较好的对于技术用的practice是Q首先知道它能ؓ(f)你做什么,它最好的应用领域Q然后再L入?strong>
QPS:如果谁有使用Drools的问题,可以联系我!一赯论!Q?br />
说明Q?/span>Jar包和c\径实际上是一个概?/span>Q比如类com.bbebfe.Test.class打包?/span>test.jar包,dcd用就直接dtest.jar包。而如果是dcL件\径,则添加包目录的上U目录,比如lib/com/bbebfe/Test.classQ则dlib文g夹,而不?/span>com文g夏V在此后的例子中都只说明Jar包的形式?/span>
分析Q?/span>Eclipse插g开发对?/span>Jar包的引用主要有三U原因:(x)
1Q?/font> 插g引用W三方包Q普通的jar包或者类文gQ不是插Ӟ(j)?/span>
a) 开发环境引用配|?/strong>Q在prject -> properties -> Java build path中设|?/span>
b) q行环境引用配置Q在plugin manifest~辑器的Runtime选项卡下?/span>classpath中添?/span>tset.jar包的引用Q在MANIFEST.MF中表Cؓ(f)Bundle-ClassPath: lib/test.jar, ?/span>plugin.xml表现?/span><runtime>节下的引?/span>
i. cL件在lib目录下,如下的设|导?/span>lib目录下所有目录:(x)
<runtime>
<library name="lib/">
<export name="*"/>
</library>
</runtime>
ii. test.jar?/span>lib目录下:(x)
<runtime>
<library name="lib/test.jar">
<export name="*"/>
</library>
</runtime>
iii. 实际上上面的讄可以化ؓ(f)Q?/span>
<runtime>
<library name="lib/"/>
</runtime>
或?/span>
<runtime>
<library name="lib/test.jar"/>
</runtime>
默认卛_?/span>lib目录下的所有包?/span>jar下的所有包
实际上,执行b)设|后Q会(x)自动执行a)设|,使开发环境和q行环境同时有效?/span>
2Q?/font> 插gB引用插g工程AQ非Eclipse插gQ而是自己另外一个插仉目中的类Q?/span>
a) 首先必须?/span>A中的B需要的cLԌexportQ出?/span>
i. 如果?/span>MANIFEST.MF文gQ则表现?/span>plugin manifest~辑器中runtime节的exported packagesQ通过q里d需?/span>export的包。在manifest.mf文g中是Export-Package: com.bbebfe
ii. 如果只有plugin.xmlQ则表现?/span>plugin manifest~辑器中runtime节的library visibility?/span>?/span>plugin.xml文g中表Cؓ(f)
<runtime>
<library>
<export name=”com.bbebfe.*”/>
...
b) ?/span>B插g工程?/span>plugin manifest~辑器中?/span>dependencies选项卡中d?/span>A插g的引用(q要求运行对话框中的plugins列表?/span>workspace plugins中必d?/span>A插gQ?/span>
c) 如果B工程是一?/span>RCP工程Q则必须?/span>product~辑器的configuration选项卡中包含A插g工程?/span>
3Q?/font> 插gB引用Eclipse插gA的类?/span>
a) Eclipse插g中的c都?/span>ExportedQ因此这步省略?/span>
b) ?/span>B插g工程?/span>plugin manifest~辑器中?/span>dependencies选项卡中d?/span>A插g的引用(q要?/span>preferences -> plugin development -> target目标q_必须包含A插gQ且q行对话框的plugins列表中的target platform中必选中A插gQ?/span>
ȝQ如?/span>B插g引用?/span>A也是一个插?/span>Q则A必须出现?/span>B插g?/span>plugin dependencies引用中,而不是其他地方,否则肯定?x)出现运行?/span>NoClassDefFoundError问题Q因此必dplugin manifest~辑器的dependencies选项卡下q行讄Q。而且只需要在q里讄的设|对开发环境和q行环境同时有效Q?/span>