作者:余賢雷 ?盧陽(yáng),單位:中國(guó)移動(dòng)智慧家庭運(yùn)營(yíng)中心
在機(jī)頂盒軟探針項(xiàng)目中,項(xiàng)目需要針對(duì)地域維度對(duì)機(jī)頂盒指標(biāo):播放成功率、收視用戶數(shù)、EPG請(qǐng)求成功率,實(shí)現(xiàn)準(zhǔn)實(shí)時(shí)的告警判定。針對(duì)告警規(guī)則的多變性,我們引入了規(guī)則引擎作為告警規(guī)則的實(shí)現(xiàn)方式。
Part 1●?規(guī)則引擎?●
規(guī)則引擎:全稱為業(yè)務(wù)規(guī)則管理系統(tǒng),英文名為BRMS。規(guī)則引擎的主要思想是將應(yīng)用程序中的業(yè)務(wù)決策部分分離出來(lái),并使用預(yù)定義的語(yǔ)義模塊編寫業(yè)務(wù)決策(業(yè)務(wù)規(guī)則),由用戶或 開(kāi)發(fā)者在需要時(shí)進(jìn)行配置、管理。需要注意的是規(guī)則引擎并不是一個(gè)具體的技術(shù)框架,而是指的一類系統(tǒng),即業(yè)務(wù)規(guī)則管理系統(tǒng)。
java開(kāi)源的規(guī)則引擎有:Drools、Easy Rules、Mandarax、IBM ILOG。使用最為廣泛并且開(kāi)源的是Drools。
主要應(yīng)用場(chǎng)景:對(duì)于一些存在比較復(fù)雜的業(yè)務(wù)規(guī)則并且業(yè)務(wù)規(guī)則會(huì)頻繁變換的系統(tǒng)比較適合使用規(guī)則引擎,如下:
風(fēng)控決策系統(tǒng)-------風(fēng)險(xiǎn)貸款、風(fēng)險(xiǎn)評(píng)估
反欺詐項(xiàng)目-----銀行貸款、征信驗(yàn)證、反洗錢
實(shí)時(shí)反欺詐平臺(tái)-----手機(jī)支付、信用卡消費(fèi)
Drools規(guī)則引擎構(gòu)成
drools規(guī)則引擎由以下幾部分構(gòu)成:
Working Memory(工作內(nèi)存)
Rules(規(guī)則庫(kù))
Inference Engine(推理引擎)
◆Pattern Match(匹配器)具體匹配那一個(gè)規(guī)則,由它來(lái)完成◆Agenda(議程)◆Execution Engine(執(zhí)行引擎)
Drools規(guī)則引擎概念
Working Memory:工作內(nèi)存,drools規(guī)則引擎會(huì)從Working Memory中獲取數(shù)據(jù)并和規(guī)則文件中定義的規(guī)則進(jìn)行模式匹配,所以我們開(kāi)發(fā)的應(yīng)用程序只需要將我們的數(shù)據(jù)插入到Working Memory中即可,例如本案例中我們調(diào)用kieSession.insert(order)就是將order對(duì)象插入到了工作內(nèi)存中。
Fact:事實(shí),是指在drools 規(guī)則應(yīng)用當(dāng)中,將一個(gè)普通的JavaBean插入到Working Memory后的對(duì)象就是Fact對(duì)象,例如本案例中的Order對(duì)象就屬于Fact對(duì)象。Fact對(duì)象是我們的應(yīng)用和規(guī)則引擎進(jìn)行數(shù)據(jù)交互的橋梁或通道。
Rules:規(guī)則庫(kù),我們?cè)谝?guī)則文件中定義的規(guī)則都會(huì)被加載到規(guī)則庫(kù)中。
Pattern Matcher:匹配器,將Rule Base中的所有規(guī)則與Working Memory中的Fact對(duì)象進(jìn)行模式匹配,匹配成功的規(guī)則將被激活并放入Agenda中。
Agenda:議程,用于存放通過(guò)匹配器進(jìn)行模式匹配后被激活的規(guī)則。
Drools 基礎(chǔ)語(yǔ)法
規(guī)則文件構(gòu)成
關(guān)鍵字 | 描述 |
package 包名 | 只限于邏輯上的管理,同一個(gè)包名下的查詢或者函數(shù)可以直接調(diào)用 |
import | 用于導(dǎo)入類或者靜態(tài)方法 |
global | 全局變量 |
function | 自定義函數(shù) |
query | 查詢 |
rule end | 規(guī)則體 |
一個(gè)規(guī)則通常包含三個(gè)部分:屬性部分(attribute)、條件部分(LHS)和結(jié)果部分(RHS)
語(yǔ)法結(jié)構(gòu)體:
rule "ruleName"
attributes
when
LHS
then
RHS
end
rule:關(guān)鍵字,表示規(guī)則開(kāi)始,參數(shù)為規(guī)則的唯一名稱。
attributes:規(guī)則屬性,是rule與when之間的參數(shù),為可選項(xiàng)。
when:關(guān)鍵字,后面跟規(guī)則的條件部分。
LHS(Left Hand Side):是規(guī)則的條件部分的通用名稱。它由零個(gè)或多個(gè)條件元素組成。如果LHS為空,則它將被視為始終為true的條件元素。
then:關(guān)鍵字,后面跟規(guī)則的結(jié)果部分。
RHS(Right Hand Side):是規(guī)則的后果或行動(dòng)部分的通用名稱。
end:關(guān)鍵字,表示一個(gè)規(guī)則結(jié)束。
Part 2●??規(guī)則比較操作符?●
符號(hào) | 說(shuō)明 |
> | 大于 |
< | 小于 |
>= | 大于等于 |
<= | 小于等于 |
== | 等于 |
!= | 不等于 |
contains | 檢查一個(gè)Fact對(duì)象的某個(gè)屬性值是否包含一個(gè)指定的對(duì)象值 |
not contains | 檢查一個(gè)Fact對(duì)象的某個(gè)屬性值是否不包含一個(gè)指定的對(duì)象值 |
memberOf | 判斷一個(gè)Fact對(duì)象的某個(gè)屬性是否在一個(gè)或多個(gè)集合中 |
not memberOf | 判斷一個(gè)Fact對(duì)象的某個(gè)屬性是否不在一個(gè)或多個(gè)集合中 |
matches | 判斷一個(gè)Fact對(duì)象的屬性是否與提供的標(biāo)準(zhǔn)的Java正則表達(dá)式進(jìn)行匹配 |
not matches | 判斷一個(gè)Fact對(duì)象的屬性是否不與提供的標(biāo)準(zhǔn)的Java正則表達(dá)式進(jìn)行匹配 |
Part 3●??Drools 規(guī)則屬性 attributes?●
Drools中提供的屬性如下表(部分屬性)
屬性名 | 說(shuō)明 |
salience | 指定規(guī)則執(zhí)行優(yōu)先級(jí) |
dialect | 指定規(guī)則使用的語(yǔ)言類型,取值為java和mvel |
enabled | 指定規(guī)則是否啟用 |
date-effective | 指定規(guī)則生效時(shí)間 |
date-expires | 指定規(guī)則失效時(shí)間 |
activation-group | 激活分組,具有相同分組名稱的規(guī)則只能有一個(gè)規(guī)則觸發(fā) |
agenda-group | 議程分組,只有獲取焦點(diǎn)的組中的規(guī)則才有可能觸發(fā) |
timer | 定時(shí)器,指定規(guī)則觸發(fā)的時(shí)間 |
auto-focus | 自動(dòng)獲取焦點(diǎn),一般結(jié)合agenda-group一起使用 |
no-loop | 防止死循環(huán),防止自己更新規(guī)則再次觸發(fā) |
lock-on-active | no-loop增強(qiáng)版本??煞乐箘e人更新規(guī)則再次出發(fā) |
Part 4●??項(xiàng)目應(yīng)用?●
省機(jī)頂盒軟探針平臺(tái)為實(shí)現(xiàn)機(jī)頂盒用戶視頻使用質(zhì)量的實(shí)時(shí)監(jiān)控,采用實(shí)時(shí)大數(shù)據(jù)進(jìn)行準(zhǔn)實(shí)時(shí)的指標(biāo)統(tǒng)計(jì),指標(biāo)結(jié)果推送kafka,由平臺(tái)新增的告警引擎實(shí)時(shí)消費(fèi)kafka數(shù)據(jù)并通過(guò)預(yù)定制的專家規(guī)則實(shí)現(xiàn)實(shí)時(shí)的告警判定,最終告警結(jié)果實(shí)時(shí)通知反饋到告警平臺(tái),實(shí)現(xiàn)告警到告警恢復(fù)的生命周期。
Part 5●??規(guī)則示例?●
rule "播放成功率告警"
no-loop true
when
$fact:Fact()
eval($fact.getPlay() > 0.0)
eval($fact.getPlay() < MapUtils.getDoubleValue($fact, "playThreshold", 0.96))
then
RuleResult fr = new RuleResult("播放成功率告警");
fr.setRuleCode(900001);
fr.setActualTime(MapUtils.getString($fact, "actualTime"));
fr.setAlarmTime(RuleUtil.getAlarmTime(MapUtils.getString($fact, "actualTime"), MapUtils.getString($fact, "900001")));
//說(shuō)明當(dāng)前是要告警的
fr.setAlarmStatus("1");
fr.setAlarmTitle("指標(biāo)播放成功率異常");
fr.setLocateInfo("指標(biāo)播放成功率異常");
fr.setAlarmText(RuleUtil.playAlarmText($fact, MapUtils.getDoubleValue($fact, "playThreshold", 0.96)));
insert(fr);
end
rule "播放成功率告警結(jié)束"
no-loop true
when
$fact:Fact()
eval($fact.getPlay() >= MapUtils.getDoubleValue($fact, "playThreshold", 0.96))
eval(MapUtils.getLongValue($fact, "900001") > 0)
eval(RuleUtil.checkAlarmTime($fact.getActualTime(), MapUtils.getLongValue($fact, "900001_time"), MapUtils.getLongValue($fact, "reThreshold", 900000)))
then
RuleResult fr = new RuleResult("播放成功率告警恢復(fù)");
fr.setRuleCode(900001);
fr.setActualTime(MapUtils.getString($fact, "actualTime"));
fr.setAlarmTime(MapUtils.getString($fact, "900001"));
fr.setAlarmStatus("0");
fr.setMsg("告警恢復(fù)");
fr.setAlarmTitle("指標(biāo)播放成功率異常");
fr.setLocateInfo("指標(biāo)播放成功率異常");
logger.info("播放告警恢復(fù)了{(lán)}", $fact);
insert(fr);