Drools 規(guī)則引擎的使用總結(jié)
前一段時(shí)間在開發(fā)了一個(gè)做文本分析的項(xiàng)目。在項(xiàng)目技術(shù)選型的過程中,嘗試使用了Drools規(guī)則引擎。讓它來作為項(xiàng)目中有關(guān)模式分析和關(guān)鍵詞匹配的任務(wù)。但后來,因?yàn)槟撤N原因,還是撇開了Drools。現(xiàn)將這個(gè)過程中使用Drools的一些經(jīng)驗(yàn)和心得記錄下來。
(一)什么時(shí)候應(yīng)該使用規(guī)則引擎
這實(shí)際是一個(gè)技術(shù)選型的問題。但這個(gè)問題又似乎是一個(gè)很關(guān)鍵的問題(一旦返工的話,你就知道這個(gè)問題是多么重要了)。不知大家有沒有過這樣的經(jīng)驗(yàn)和體會(huì)。往往在項(xiàng)目開始的時(shí)候,總會(huì)遇到應(yīng)該選用什么技術(shù)?是不是應(yīng)該使用最新的技術(shù)?或者應(yīng)該選用什么技術(shù)呢(PS:現(xiàn)在計(jì)算機(jī)軟件中的各種技術(shù)層出不窮,具有類似功能的技術(shù)很多)?
不管怎么樣,這些問題總會(huì)困擾著我。比如,這次的這個(gè)項(xiàng)目。項(xiàng)目要求是要在一些log文件中(這些log文件都是很大的應(yīng)用系統(tǒng)所產(chǎn)生的,但由于legacy的原因,log本身的維護(hù)和規(guī)范工作一直沒有得到改善,所以想借助于一些外部應(yīng)用對(duì)這些log做以分析和清洗)抽取出有用的信息。
于是,第一個(gè)想到的就是,這是一個(gè)文本挖掘類的項(xiàng)目。但又想,要抽取有用信息,必須得建立一些規(guī)則或pattern(模式)。所以,我第一個(gè)想到了規(guī)則引擎。因?yàn)檫@里面要建立好多規(guī)則,而這些規(guī)則可以獨(dú)立于代碼級(jí)別(放到一個(gè)單獨(dú)的drl文件里)并可以用規(guī)則引擎去解析和執(zhí)行。另一個(gè)重要的原因是,我原來用過,比較熟悉。這樣,也可以節(jié)省開發(fā)時(shí)間吧。于是,好不猶豫的就開始做了Demo....
但事實(shí)上,在經(jīng)歷了一個(gè)多星期的編碼、測(cè)試后,我發(fā)現(xiàn)運(yùn)用規(guī)則引擎實(shí)在是太笨拙了。
(1)首先必須建立一些數(shù)據(jù)模型。通過這些模型來refer規(guī)則文件中的LHS和Action。
(2)還要考慮規(guī)則的conflict。如果有一些規(guī)則同時(shí)被觸發(fā),就要考慮設(shè)定規(guī)則的優(yōu)先級(jí)或者是設(shè)定activiation-group來保證在一個(gè)group中的規(guī)則只有一個(gè)規(guī)則可以被觸發(fā)。
(3)對(duì)于‘流’規(guī)則group ruleflow-group的使用。如果要控制在workingmemory中的規(guī)則被觸發(fā)的順序,則可以將這些規(guī)則分組。然后,通過規(guī)則建模的方式來實(shí)現(xiàn)。但這也添加了一定的effort。修改或者更新不大方便。
所以,基于上述體會(huì),我更認(rèn)為規(guī)則引擎更適用于那些對(duì)非流程性規(guī)則匹配的應(yīng)用。當(dāng)然,Drools也支持對(duì)流程性規(guī)則的建模過程。但,這也許不是最好的方式。
(二)Drools規(guī)則引擎的使用雜記
(1)Fact 的變更監(jiān)聽。在Drools里,如果一個(gè)Fact通過規(guī)則而改變,則需將這種改變通知給規(guī)則引擎。這里,一般有兩種方式:顯式和隱式。
顯式---在drl文件中通過 update、modify來通知;在程序中,通過Fact的引用調(diào)用modifyObject等方法來實(shí)現(xiàn)。
隱式---通過在java bean實(shí)現(xiàn)property Listener Interface來讓引擎自動(dòng)監(jiān)聽到屬性值的變化。我更習(xí)慣于這種方式。因?yàn)椋话憧磥矸彩窃谝?guī)則引擎中添加到fact都是希望引擎來幫你進(jìn)行管理的。所以,那它自己看到fact的變化是種很省事的辦法。也很簡(jiǎn)單,就是用java bean property 監(jiān)聽的方式。
通過StatefulSession來注冊(cè)。
調(diào)用StatefulSession的某個(gè)instance 的insert(Object,true)實(shí)現(xiàn)。而這個(gè)object是一個(gè)java bean。其中,要實(shí)現(xiàn)
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方法中調(diào)用
this.changes.firePropertyChange( "temp",null,this.temp );
(2)規(guī)則觸發(fā)的優(yōu)先級(jí)、組設(shè)置
往往,在設(shè)計(jì)我們自己的規(guī)則時(shí),要考慮規(guī)則的觸發(fā)條件。這不僅限于LHS的條件部分,還有規(guī)則本身被觸發(fā)的有些設(shè)置等等。這里,列出一些比較常用和有效的規(guī)則優(yōu)先級(jí)設(shè)置方式,以及需要注意的地方。
A.通過Salience方式。此值可正可負(fù)。越大優(yōu)先級(jí)越高,也會(huì)被引擎首先執(zhí)行。
B.通過ruleflow-group 方式。實(shí)際上,使用這種方式也就是在使用建立規(guī)則流的方式。在Eclipse 3.3 中,Drools提供了建立規(guī)則流的插件。要在drl的同級(jí)目錄中建立rf和rfm兩個(gè)文件(當(dāng)然,插件會(huì)幫助你建立這些)。

選擇RuleFlow File。
這里,需要注意的一點(diǎn)是要在啟動(dòng)規(guī)則引擎的時(shí)候,加入啟動(dòng)rule flow的代碼。
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);
然后,在每次啟動(dòng)規(guī)則引擎的時(shí)候,調(diào)用如下方法:
StatefulSession ss;
ss.startProcess(flowProgress);
ss.fireAllRules();
flowProgress 是一個(gè)string類型。這個(gè)flow的名字。

這個(gè)rule flow圖中,顯示了一個(gè)簡(jiǎn)單的規(guī)則流。如RSA就是一個(gè)rule-flow的名字。在這個(gè)rule set中可以設(shè)定一組rules。這樣,就可以分開規(guī)則執(zhí)行的順序。在于rf和rfm同名的另一個(gè) drl文件中定義這些組的名字。通過關(guān)鍵字 ruleflow-group 來表明。
C.通過activation-group的方式。通過這種方式,可以exclude一組rule中一旦有一個(gè)rule被invoke,而其它rule不會(huì)被execute。同時(shí),可以搭配使用salience關(guān)鍵字來標(biāo)明每個(gè)rule的優(yōu)先級(jí),這樣就能夠使得你想要的一般性概念的rule先被匹配執(zhí)行。
D.在使用ruleflow-group 的時(shí)候要注意使用lock-on-active true 關(guān)鍵字在每個(gè)rule。這樣可以避免一旦有rule被觸發(fā),不會(huì)造成循環(huán)匹配執(zhí)行。
E.如果在LHS部分,需要調(diào)用某個(gè)方法來返回真、假值作為判斷的一個(gè)條件,那么可以用eval函數(shù)。
如,eval(pattern.matched(5,$line.getCurrLine()))
其中,pattern是某個(gè)加入到workingmemory中的一個(gè)實(shí)例。matched是這個(gè)實(shí)例所代表類的一個(gè)方法。它返回boolean類型。
(3)Drools規(guī)則引擎的使用感受
總之,Drools還是一個(gè)很不錯(cuò)的開源規(guī)則引擎。現(xiàn)在v4.0以上的版本已經(jīng)比以前的版本在速度上有了很大的提升。可以作為我們一般應(yīng)用程序系統(tǒng)的中間件產(chǎn)品(那些規(guī)則不是很經(jīng)常改變的系統(tǒng),已經(jīng)非流程類規(guī)則)。但是,這其中還是需要一些額外的effort來學(xué)習(xí)它的使用文檔以及整體架構(gòu),有一定的學(xué)習(xí)曲線。
最后,我想一個(gè)較好的對(duì)于技術(shù)使用的practice就是:首先知道它能為你做什么,它最好的應(yīng)用領(lǐng)域,然后再去深入。
(PS:如果誰有使用Drools的問題,可以聯(lián)系我!一起討論!)
(一)什么時(shí)候應(yīng)該使用規(guī)則引擎
這實(shí)際是一個(gè)技術(shù)選型的問題。但這個(gè)問題又似乎是一個(gè)很關(guān)鍵的問題(一旦返工的話,你就知道這個(gè)問題是多么重要了)。不知大家有沒有過這樣的經(jīng)驗(yàn)和體會(huì)。往往在項(xiàng)目開始的時(shí)候,總會(huì)遇到應(yīng)該選用什么技術(shù)?是不是應(yīng)該使用最新的技術(shù)?或者應(yīng)該選用什么技術(shù)呢(PS:現(xiàn)在計(jì)算機(jī)軟件中的各種技術(shù)層出不窮,具有類似功能的技術(shù)很多)?
不管怎么樣,這些問題總會(huì)困擾著我。比如,這次的這個(gè)項(xiàng)目。項(xiàng)目要求是要在一些log文件中(這些log文件都是很大的應(yīng)用系統(tǒng)所產(chǎn)生的,但由于legacy的原因,log本身的維護(hù)和規(guī)范工作一直沒有得到改善,所以想借助于一些外部應(yīng)用對(duì)這些log做以分析和清洗)抽取出有用的信息。
于是,第一個(gè)想到的就是,這是一個(gè)文本挖掘類的項(xiàng)目。但又想,要抽取有用信息,必須得建立一些規(guī)則或pattern(模式)。所以,我第一個(gè)想到了規(guī)則引擎。因?yàn)檫@里面要建立好多規(guī)則,而這些規(guī)則可以獨(dú)立于代碼級(jí)別(放到一個(gè)單獨(dú)的drl文件里)并可以用規(guī)則引擎去解析和執(zhí)行。另一個(gè)重要的原因是,我原來用過,比較熟悉。這樣,也可以節(jié)省開發(fā)時(shí)間吧。于是,好不猶豫的就開始做了Demo....
但事實(shí)上,在經(jīng)歷了一個(gè)多星期的編碼、測(cè)試后,我發(fā)現(xiàn)運(yùn)用規(guī)則引擎實(shí)在是太笨拙了。
(1)首先必須建立一些數(shù)據(jù)模型。通過這些模型來refer規(guī)則文件中的LHS和Action。
(2)還要考慮規(guī)則的conflict。如果有一些規(guī)則同時(shí)被觸發(fā),就要考慮設(shè)定規(guī)則的優(yōu)先級(jí)或者是設(shè)定activiation-group來保證在一個(gè)group中的規(guī)則只有一個(gè)規(guī)則可以被觸發(fā)。
(3)對(duì)于‘流’規(guī)則group ruleflow-group的使用。如果要控制在workingmemory中的規(guī)則被觸發(fā)的順序,則可以將這些規(guī)則分組。然后,通過規(guī)則建模的方式來實(shí)現(xiàn)。但這也添加了一定的effort。修改或者更新不大方便。
所以,基于上述體會(huì),我更認(rèn)為規(guī)則引擎更適用于那些對(duì)非流程性規(guī)則匹配的應(yīng)用。當(dāng)然,Drools也支持對(duì)流程性規(guī)則的建模過程。但,這也許不是最好的方式。
(二)Drools規(guī)則引擎的使用雜記
(1)Fact 的變更監(jiān)聽。在Drools里,如果一個(gè)Fact通過規(guī)則而改變,則需將這種改變通知給規(guī)則引擎。這里,一般有兩種方式:顯式和隱式。
顯式---在drl文件中通過 update、modify來通知;在程序中,通過Fact的引用調(diào)用modifyObject等方法來實(shí)現(xiàn)。
隱式---通過在java bean實(shí)現(xiàn)property Listener Interface來讓引擎自動(dòng)監(jiān)聽到屬性值的變化。我更習(xí)慣于這種方式。因?yàn)椋话憧磥矸彩窃谝?guī)則引擎中添加到fact都是希望引擎來幫你進(jìn)行管理的。所以,那它自己看到fact的變化是種很省事的辦法。也很簡(jiǎn)單,就是用java bean property 監(jiān)聽的方式。
通過StatefulSession來注冊(cè)。
調(diào)用StatefulSession的某個(gè)instance 的insert(Object,true)實(shí)現(xiàn)。而這個(gè)object是一個(gè)java bean。其中,要實(shí)現(xiàn)
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方法中調(diào)用
this.changes.firePropertyChange( "temp",null,this.temp );
(2)規(guī)則觸發(fā)的優(yōu)先級(jí)、組設(shè)置
往往,在設(shè)計(jì)我們自己的規(guī)則時(shí),要考慮規(guī)則的觸發(fā)條件。這不僅限于LHS的條件部分,還有規(guī)則本身被觸發(fā)的有些設(shè)置等等。這里,列出一些比較常用和有效的規(guī)則優(yōu)先級(jí)設(shè)置方式,以及需要注意的地方。
A.通過Salience方式。此值可正可負(fù)。越大優(yōu)先級(jí)越高,也會(huì)被引擎首先執(zhí)行。
B.通過ruleflow-group 方式。實(shí)際上,使用這種方式也就是在使用建立規(guī)則流的方式。在Eclipse 3.3 中,Drools提供了建立規(guī)則流的插件。要在drl的同級(jí)目錄中建立rf和rfm兩個(gè)文件(當(dāng)然,插件會(huì)幫助你建立這些)。
選擇RuleFlow File。
這里,需要注意的一點(diǎn)是要在啟動(dòng)規(guī)則引擎的時(shí)候,加入啟動(dòng)rule flow的代碼。
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);
然后,在每次啟動(dòng)規(guī)則引擎的時(shí)候,調(diào)用如下方法:
StatefulSession ss;
ss.startProcess(flowProgress);
ss.fireAllRules();
flowProgress 是一個(gè)string類型。這個(gè)flow的名字。

這個(gè)rule flow圖中,顯示了一個(gè)簡(jiǎn)單的規(guī)則流。如RSA就是一個(gè)rule-flow的名字。在這個(gè)rule set中可以設(shè)定一組rules。這樣,就可以分開規(guī)則執(zhí)行的順序。在于rf和rfm同名的另一個(gè) drl文件中定義這些組的名字。通過關(guān)鍵字 ruleflow-group 來表明。
C.通過activation-group的方式。通過這種方式,可以exclude一組rule中一旦有一個(gè)rule被invoke,而其它rule不會(huì)被execute。同時(shí),可以搭配使用salience關(guān)鍵字來標(biāo)明每個(gè)rule的優(yōu)先級(jí),這樣就能夠使得你想要的一般性概念的rule先被匹配執(zhí)行。
D.在使用ruleflow-group 的時(shí)候要注意使用lock-on-active true 關(guān)鍵字在每個(gè)rule。這樣可以避免一旦有rule被觸發(fā),不會(huì)造成循環(huán)匹配執(zhí)行。
E.如果在LHS部分,需要調(diào)用某個(gè)方法來返回真、假值作為判斷的一個(gè)條件,那么可以用eval函數(shù)。
如,eval(pattern.matched(5,$line.getCurrLine()))
其中,pattern是某個(gè)加入到workingmemory中的一個(gè)實(shí)例。matched是這個(gè)實(shí)例所代表類的一個(gè)方法。它返回boolean類型。
(3)Drools規(guī)則引擎的使用感受
總之,Drools還是一個(gè)很不錯(cuò)的開源規(guī)則引擎。現(xiàn)在v4.0以上的版本已經(jīng)比以前的版本在速度上有了很大的提升。可以作為我們一般應(yīng)用程序系統(tǒng)的中間件產(chǎn)品(那些規(guī)則不是很經(jīng)常改變的系統(tǒng),已經(jīng)非流程類規(guī)則)。但是,這其中還是需要一些額外的effort來學(xué)習(xí)它的使用文檔以及整體架構(gòu),有一定的學(xué)習(xí)曲線。
最后,我想一個(gè)較好的對(duì)于技術(shù)使用的practice就是:首先知道它能為你做什么,它最好的應(yīng)用領(lǐng)域,然后再去深入。
(PS:如果誰有使用Drools的問題,可以聯(lián)系我!一起討論!)
posted on 2008-11-04 14:19 Jeffrey Feng 閱讀(13643) 評(píng)論(13) 編輯 收藏 所屬分類: 開源技術(shù)