drools規則屬性(rule attributes)的使用
一、介紹
規則屬性
是您可以添加到業務規則以修改規則行為
的附加規範。 在 DRL
文件中,您通常在規則條件和操作的上方定義規則屬性,多個屬性位於單獨的行中
,格式如下:
rule "rule_name"
// Attribute
// Attribute
when
// Conditions
then
// Actions
end
二、常見的規則屬性
規則屬性 | 解釋 | 舉例 |
---|---|---|
salience | 定義規則優先順序 ,是一個整數。當在激活隊列中排序 時,salience的值越大 ,優先順序越高 。 |
salience 99 |
enabled | 定義規則是否啟用. true 啟用,false 禁用,默認值是true |
enabled true |
date-effective | 包含時間和日期的字元串,噹噹前時間大於date-effective 時,該規則才會被激活。這個時間格式可以修改,見下方具體的用法 |
date-effective “4-5月-2022” |
date-expires | 設置規則的過期時間,時間格式和上方一樣。 | date-expires “4-5月-2022” |
no-loop | 布爾值,默認值為false , 定義當當前規則規則的結果 修改了fact 對象時,是否可以再次執行該規則。true: 不可以, false: 可以,可能會導致死循環。指的是當前規則的修改,如果別的規則修改了,還會導致該規則的觸發 |
no-loop true |
agenda-group | Agenda groups 允許您對agenda 進行分區,以提供對規則組的更多執行控制。 只有獲得焦點的議程組中的規則才能被激活。 ,但是這個裡面有個特例 ,如果某個規則沒有配置 agenda-group,但是它模式匹配成功了,那麼會被分到默認的組(main ),這個main 組的規則也會執行。 |
agenda-group “GroupName” |
auto-focus | 布爾值,僅適用於Agenda-Group 內的規則。當值為true 時,下次激活該規則時,會將焦點自動給這個Agenda group |
auto-focus true |
activation-group | 表示該組下的規則只有一個規則 會被執行,該組下其餘激活的規則會被取消執行。 但是別的組激活的規則可能會被執行。 |
activation-group “GroupName” |
duration | long類型的值,如果在這個時間之後規則還成立 ,那麼執行該規則 |
duration 1000 |
timer | 一個字元串,標識用於調度規則的 int(間隔)或 cron 計時器定義。 | Example: timer ( cron:* 0/15 * * * ? ) (every 15 minutes) |
calendar | 定義Quartz calendar用於調度規則。 | |
lock-on-active | 一個布爾值,僅適用於規則流組或議程組中的規則。 選擇該選項後,下次規則的規則流組變為活動狀態或規則的議程組獲得焦點時,規則無法再次激活,直到規則流組不再處於活動狀態或議程組失去焦點。 這是 no-loop 屬性的更強版本,因為匹配規則的激活被丟棄,無論更新的來源如何(不僅是規則本身)。 此屬性非常適合計算規則,其中您有許多修改事實的規則並且您不希望任何規則重新匹配和再次觸發。 | lock-on-active true |
dialect | 將 JAVA 或 MVEL 標識為用於規則中的程式碼表達式的語言的字元串。 默認情況下,該規則使用在包級別指定的方言。 此處指定的任何方言都會覆蓋該規則的包方言設置。 | dialect “JAVA” |
三、部分規則屬性案例
此處編寫出規則文件和部分核心Java程式碼
1、salience
定義規則執行的優先順序,salience的值越大,優先順序越高
1、規則文件的編寫
rule "salience_rule_1"
salience 4
when
then
System.out.println("rule 1");
end
rule "salience_rule_2"
salience 3
when
then
System.out.println("rule 2");
end
// 此處優先順序的值是動態獲取來的
rule "salience_rule_3"
salience $dynamicSalience
when
$dynamicSalience: Integer()
then
System.out.println("rule 3");
end
注意:
我們的salience_rule_3
的優先順序的值是動態
來的,即是從工作記憶體中獲取的。
2、java程式碼編寫
public class DroolsSalienceApplication {
public static void main(String[] args) {
KieServices kieServices = KieServices.get();
KieContainer kieContainer = kieServices.getKieClasspathContainer();
KieSession kieSession = kieContainer.newKieSession("rule-attributes-ksession");
// 向工作記憶體中插入一個Integer值,salience_rule_3 需要用到這個優先順序
kieSession.insert(10);
// 只匹配規則名稱是已 salience_ 開頭的規則,忽略其餘的規則
kieSession.fireAllRules(new RuleNameStartsWithAgendaFilter("salience_"));
kieSession.dispose();
}
}
kieSession.insert(10);
此處向工作記憶體中插入一個值,將會匹配到salience_rule_3
,然後動態修改它的優先順序。
3、運行結果
rule 3
rule 1
rule 2
因為 salience
的值越大優先順序越高,所以是這個順序。
2、enabled
定義規則是否啟用,true
啟用 false
禁用
1、規則文件編寫
package rules
rule "enabled_rule_1"
// 禁用此規則
enabled false
when
then
System.out.println("enabled_rule_1");
end
rule "enabled_rule_2"
// 啟用此規則,默認就是啟用
enabled true
when
then
System.out.println("enabled_rule_2");
end
enabled_rule_2
這個規則需要運行,enabled_rule_1
這個規則不能運行。
2、java程式碼編寫
/**
* 測試規則的啟用和禁用
*/
public class DroolsEnabledApplication {
public static void main(String[] args) {
KieServices kieServices = KieServices.get();
KieContainer kieContainer = kieServices.getKieClasspathContainer();
KieSession kieSession = kieContainer.newKieSession("rule-attributes-ksession");
// 只匹配規則名稱是已 enabled_ 開頭的規則,忽略其餘的規則
kieSession.fireAllRules(new RuleNameStartsWithAgendaFilter("enabled_"));
kieSession.dispose();
}
}
沒有需要注意的地方
3、運行結果
enabled_rule_2
可以看到只有規則enabled_rule_2
輸出了結果,而enabled_rule_1
被禁用了。
3、date-effective
定義規則什麼時候啟用,只有當前時間>
規則時間才會啟用。需要注意默認的時間格式
,可以通過java程式碼進行修改。
1、規則文件編寫
package rules
import java.text.SimpleDateFormat
import java.util.Date
// 規則一:輸出當前時間
rule "date_effective_rule_1"
when
then
System.out.println("當前時間:" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
end
// 規則二: 該規則會在2022-05-18 10:54:26之後被激活
rule "date_effective_rule_2"
date-effective "2022-05-18 10:54:26"
when
then
System.out.println("date_effective_rule_2執行了,規則允許被執行的時間應該在2022-05-18 10:54:26之後");
end
// 規則三: 該規則會在2023-05-18 10:54:26之後被激活
rule "date_effective_rule_3"
date-effective "2023-05-18 10:54:26"
when
then
System.out.println("date_effective_rule_3會在時間到了2023-05-18 10:54:26才激活");
end
規則一:輸出當前時間
規則二: 該規則會在2022-05-18 10:54:26之後被激活
規則三: 該規則會在2023-05-18 10:54:26之後被激活
2、java程式碼編寫
/**
* 測試規則在執行的時間之後才能執行
*/
public class DroolsDateEffectiveApplication {
public static void main(String[] args) {
// 設置日期格式,否則可能會報錯(Wrong date-effective value: Invalid date input format: [2022-05-18 10:54:26] it should follow: [d-MMM-yyyy]]])
System.setProperty("drools.dateformat", "yyyy-MM-dd HH:mm:ss");
KieServices kieServices = KieServices.get();
KieContainer kieContainer = kieServices.getKieClasspathContainer();
KieSession kieSession = kieContainer.newKieSession("rule-attributes-ksession");
// 只匹配規則名稱是已 date_effective_ 開頭的規則,忽略其餘的規則
kieSession.fireAllRules(new RuleNameStartsWithAgendaFilter("date_effective_"));
kieSession.dispose();
}
}
需要注意System.setProperty("drools.dateformat", "yyyy-MM-dd HH:mm:ss");
這句,這個修改drools中的日期格式,因為規則中寫的日期格式為date-effective "2023-05-18 10:54:26"
而默認的格式為d-MMM-yyyy
,不修會報錯。
3、運行結果
當前時間:2022-05-18 10:59:38
date_effective_rule_2執行了,規則允許被執行的時間應該在2022-05-18 10:54:26之後
可以看到規則二執行了,規則三沒有執行,因為規則三需要時間到達了2023-05-18 10:54:26
才執行,而當前時間不符合。
4、注意事項
如果出現了Wrong date-effective value: Invalid date input format: [2022-05-18 10:54:26] it should follow: [d-MMM-yyyy]]]
這個錯誤該怎麼解決了,這是因為日期格式不正確。需要在java程式碼中進行如下設置System.setProperty("drools.dateformat", "yyyy-MM-dd HH:mm:ss")
4、date-expires
定義規則的過期時間,即規則到了該時間之後就不可使用了。和date-effective
的用法類似,此處就不演示了。
5、no-loop
定義當當前規則
的結果修改了fact
對象時,是否可以再次執行該規則。可以防止死循環
1、規則文件編寫
package rules
import java.util.concurrent.TimeUnit
import java.text.SimpleDateFormat
import java.util.Date
rule "no_loop_rule_1"
no-loop true
when
$i: Integer(intValue() < 20)
then
modify($i){
}
System.out.println(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()) + " no_loop_rule_1 i=" + $i);
end
rule "no_loop_rule_2"
no-loop false
when
$i: Integer(intValue() < 20)
then
modify($i){
}
TimeUnit.SECONDS.sleep(1);
System.out.println(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()) + " no_loop_rule_2 i=" + $i);
end
解釋:
no_loop_rule_1
:no-loop true
表示如果當前規則的RHS
部分,對Fact對象進行了修改,則不會再次觸發該規則。那如果是no_loop_rule_2
修改了,會導致該規則的觸發嗎?答案是
會觸發,如果我不想被觸發呢?那麼使用lock-on-active
可以實現。
no_loop_rule_2
:no-loop false
表示如果當前規則的RHS
部分,對Fact對象進行了修改,那麼還會再次匹配這個規則。
2、java程式碼編寫
/**
* 測試規則是否可以再次被執行
*/
public class DroolsNoLoopApplication {
public static void main(String[] args) throws InterruptedException {
System.setProperty("drools.dateformat", "yyyy-MM-dd HH:mm:ss");
KieServices kieServices = KieServices.get();
KieContainer kieContainer = kieServices.getKieClasspathContainer();
KieSession kieSession = kieContainer.newKieSession("rule-attributes-ksession");
kieSession.insert(10);
// 只匹配規則名稱是已 no_loop_ 開頭的規則,忽略其餘的規則
kieSession.fireAllRules(new RuleNameStartsWithAgendaFilter("no_loop_"));
// 睡眠5s,使規則文件中的規則執行完
TimeUnit.SECONDS.sleep(5);
kieSession.dispose();
}
}
此處 java 程式碼,睡眠了5s,是為了讓規則執行。
3、運行結果
2022-05-18 11:42:29 no_loop_rule_1 i=10
2022-05-18 11:42:31 no_loop_rule_2 i=10
2022-05-18 11:42:31 no_loop_rule_1 i=10
2022-05-18 11:42:32 no_loop_rule_2 i=10
解釋:
2022-05-18 11:42:29 no_loop_rule_1 i=10
: no_loop_rule_1被觸發,由於RHS部分使用了modify
修改了規則記憶體中的對象,但是該規則存在 no-loop true 的屬性,所以該規則沒有再次被觸發,即只輸出了一次。
2022-05-18 11:42:30 no_loop_rule_2 i=10 2022-05-18 11:42:30 no_loop_rule_1 i=10
此時規則 no_loop_rule_2 執行了,由於該規則的 no-loop 為 false 並且使用了 modify 方法,所以該規則多次被觸發了,從結果上看,貌似規則 no_loop_rule_1 又再次被觸發了,不是應該不被觸發嗎,因為設置了no-loop true?因為這是no_loop_rule_2導致no_loop_rule_1觸發的,而no_loop只對自身的RHS修改有效。
疑問:
那如果將 no-loop
換成lock-on-active
結果會一樣嗎?可以自己嘗試一下看看結果。
6、agenda-group
將被模式匹配成功後的規則,進行分組,只有獲得焦點的組
,才可以執行規則。但是這個裡面有個特列,如果某個規則在模式匹配,匹配成功了,但是沒有配置agenda-group,那麼它會被分配到main
組,這個main
組的規則總是執行的。
agenda-group
的數據結構就類似stack
,激活的組是在棧頂。參考如下圖:
參考鏈接: //stackoverflow.com/questions/6870192/understanding-agenda-group-in-drools
1、規則文件編寫
package rules
/**
agenda-group 的數據結構類似與棧,激活的組會被放置在棧頂,
`main`是默認組,總是存在的,即沒有配置agenda-group的就是`main`,
`main`總是會執行的。
*/
rule "agenda_group_001_rule_1"
agenda-group "group-001"
when
then
System.out.println("agenda_group_001_rule_1");
end
rule "agenda_group_001_rule_2"
agenda-group "group-001"
when
then
System.out.println("agenda_group_001_rule_2");
end
rule "agenda_group_002_rule_3"
agenda-group "group-002"
when
then
System.out.println("agenda_group_002_rule_3");
end
rule "agenda_group_no_group_rule_4"
when
then
System.out.println("agenda_group_no_group_rule_4");
end
注意: 此處其實是 存在 3個組的,agenda_group_no_group_rule_4
如果模式匹配成功後會被分配到main
組,main
總是會被執行的。
2、java程式碼編寫
/**
* 測試規則分組
*/
public class DroolsAgendaGroupApplication {
public static void main(String[] args) {
// 設置日期格式,否則可能會報錯(Wrong date-effective value: Invalid date input format: [2022-05-18 10:54:26] it should follow: [d-MMM-yyyy]]])
System.setProperty("drools.dateformat", "yyyy-MM-dd HH:mm:ss");
KieServices kieServices = KieServices.get();
KieContainer kieContainer = kieServices.getKieClasspathContainer();
KieSession kieSession = kieContainer.newKieSession("rule-attributes-ksession");
// 激活組
kieSession.getAgenda().getAgendaGroup("group-001").setFocus();
// 只匹配規則名稱是已 agenda_group_ 開頭的規則,忽略其餘的規則
kieSession.fireAllRules(new RuleNameStartsWithAgendaFilter("agenda_group_"));
kieSession.dispose();
}
}
激活group-001
分組。
3、運行結果
agenda_group_001_rule_1
agenda_group_001_rule_2
agenda_group_no_group_rule_4
解釋:
agenda_group_no_group_rule_4
為什麼會被輸出呢?它沒有定義agenda-group
啊,而且我們激活的也是group-001
分組,它不應該輸出啊。這是應為這個規則模式匹配成功後被分配到了默認的main
組,而main
組一定會被執行的。
7、auto-focus
設置某個agenda-group
默認獲取到焦點,和在java程式碼中使用kieSession.getAgenda().getAgendaGroup("group-001").setFocus();
或在drl文件中使用drools.setFocus(..)
一樣。
8、activation-group
處於該分組中激活的規則,同一個組下,只有一個規則可以執行
,其餘的會被取消執行。但是別的組中激活的規則還是可以執行的。
1、規則文件編寫
package rules
rule "activation_group_001_rule_1"
activation-group "group-001"
salience 1
when
then
System.out.println("activation_group_001_rule_1");
end
rule "activation_group_001_rule_2"
activation-group "group-001"
salience 2
when
then
System.out.println("activation_group_001_rule_2");
end
rule "activation_group_002_rule_3"
activation-group "group-002"
when
then
System.out.println("activation_group_002_rule_3");
end
rule "activation_group_no_group_rule_4"
when
then
System.out.println("activation_group_no_group_rule_4");
end
activation-group "group-001"
此處對這個組的規則指定了優先順序,優先順序高的先執行,執行完之後,該組別的規則不執行。
2、java程式碼編寫
public class DroolsActivationGroupApplication {
public static void main(String[] args) {
System.setProperty("drools.dateformat", "yyyy-MM-dd HH:mm:ss");
KieServices kieServices = KieServices.get();
KieContainer kieContainer = kieServices.getKieClasspathContainer();
KieSession kieSession = kieContainer.newKieSession("rule-attributes-ksession");
// 只匹配規則名稱是已 activation_group_ 開頭的規則,忽略其餘的規則
kieSession.fireAllRules(new RuleNameStartsWithAgendaFilter("activation_group_"));
kieSession.dispose();
}
}
3、運行結果
activation_group_001_rule_2
activation_group_002_rule_3
activation_group_no_group_rule_4
可以看到分組group-001
中有2個規則,但是只執行了一個規則。
9、duration
long類型的值,單位毫秒,如果在這個時間之後規則還成立
,那麼執行該規則。
1、規則文件編寫
package rules
import java.text.SimpleDateFormat
import java.util.Date
rule "duration_rule_1"
// 延遲1s後執行規則
duration 1000
when
$i: Integer(intValue() < 10)
then
System.out.println(Thread.currentThread().getName() + ": " +
new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())+
" duration_rule_1 $i:"+$i);
end
定義規則延遲1s
後進行執行。
2、java程式碼編寫
/**
* 在多少毫秒後,如果條件還成立,則觸發該規則
*/
public class DroolsDurationApplication {
public static void main(String[] args) throws InterruptedException {
System.setProperty("drools.dateformat", "yyyy-MM-dd HH:mm:ss");
KieServices kieServices = KieServices.get();
KieContainer kieContainer = kieServices.getKieClasspathContainer();
KieSession kieSession = kieContainer.newKieSession("rule-attributes-ksession");
FactHandle factHandle = kieSession.insert(3);
// 只匹配規則名稱是已 duration_ 開頭的規則,忽略其餘的規則
new Thread(() -> {
// 調用此方法會阻塞調用執行緒,直到 `kieSession.halt();`的調用
System.out.println("當前時間:" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
kieSession.fireUntilHalt(new RuleNameStartsWithAgendaFilter("duration_"));
}, "fire-thread").start();
// 如果修改這個值,使得規則的條件不成立,看規則是否還執行
kieSession.update(factHandle, 4);
TimeUnit.SECONDS.sleep(2);
kieSession.halt();
kieSession.dispose();
}
}
注意:
1、我們調用出發所有規則執行的方法不在是fireAllRules
而是fireUntilHalt
。
2、fireUntilHalt
的調用會阻塞執行緒,直到調用halt
方法,因此fireUntilHalt
需要放置到另外的執行緒中調用。而且我們觀察規則的執行,也是在這個執行緒中調用的。
3、運行結果
當前時間:2022-05-18 14:13:36
fire-thread: 2022-05-18 14:13:37 duration_rule_1 $i:4
可以看到,延遲1s
後規則執行了。
4、疑問
如果我們在1s
鍾之內,將規則的條件修改成不成立,那麼規則還執行嗎?答案:
不執行。
10、lock-on-active
和rule flow groups or agenda groups
配合使用。
需求:
我們有2個規則,並且同屬於一個組,規則二執行完之後,工作記憶體中的Fact對象的值發生了變化,導致規則一滿足執行的條件,而規則一已經執行一遍了,此處需要阻止規則二的觸發導致規則一的出觸發。使用lock-on-active
即可實現。
1、規則文件編寫
package rules
import com.huan.drools.lockonactive.Person
rule "lock_on_active_rule_01"
agenda-group "group-001"
lock-on-active true
when
$p: Person(age < 18)
then
System.out.println("lock_on_active_rule_01: 用戶:[" + $p.getName() + "]當前的年齡是:[" + $p.getAge() + "]");
end
rule "lock_on_active_rule_02"
agenda-group "group-001"
when
$p: Person(name == "張三")
then
modify($p){
setAge(15)
}
System.out.println("lock_on_active_rule_02: 用戶:[" + $p.getName() + "]當前的年齡是:[" + $p.getAge() + "]");
end
規則lock_on_active_rule_01
加了lock-on-active true
屬性後,規則lock_on_active_rule_02
修改Fact
導致規則lock_on_active_rule_01
的條件成立,此時規則也是不會執行的。
2、java程式碼編寫
/**
* 一個簡單的實體類
*
* @author huan.fu
* @date 2022/5/18 - 14:34
*/
@Getter
@Setter
@AllArgsConstructor
public class Person {
private String name;
private Integer age;
}
public class DroolsLockOnActiveApplication {
public static void main(String[] args) {
System.setProperty("drools.dateformat", "yyyy-MM-dd HH:mm:ss");
KieServices kieServices = KieServices.get();
KieContainer kieContainer = kieServices.getKieClasspathContainer();
KieSession kieSession = kieContainer.newKieSession("rule-attributes-ksession");
// 激活組
kieSession.getAgenda().getAgendaGroup("group-001").setFocus();
Person person = new Person("張三", 20);
kieSession.insert(person);
// 只匹配規則名稱是已 lock_on_active_ 開頭的規則,忽略其餘的規則
kieSession.fireAllRules(new RuleNameStartsWithAgendaFilter("lock_on_active_"));
kieSession.dispose();
}
}
3、運行結果
lock_on_active_rule_02: 用戶:[張三]當前的年齡是:[15]
可以看到只有規則二執行了,說明阻止了規則一的執行。
四、完整程式碼
//gitee.com/huan1993/spring-cloud-parent/tree/master/drools/drools-drl-rule-attributes
五、參考鏈接
1、//docs.drools.org/7.69.0.Final/drools-docs/html_single/index.html#rules-attributes-ref_drl-rules
2、 //stackoverflow.com/questions/6870192/understanding-agenda-group-in-drools