欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

drools規(guī)則動態(tài)化實(shí)踐解析

 更新時間:2023年02月07日 08:23:58   作者:京東云開發(fā)者  
這篇文章主要為大家介紹了drools規(guī)則動態(tài)化實(shí)踐解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

一 、 規(guī)則引擎業(yè)務(wù)應(yīng)用背景

業(yè)務(wù)邏輯中經(jīng)常會有一些冗長的判斷,需要寫特別多的if else,或者一些判斷邏輯需要經(jīng)常修改。這部分邏輯如果以java代碼來實(shí)現(xiàn),會面臨代碼規(guī)??刂撇蛔?,經(jīng)常需要修改邏輯上線等多個弊端。這時候我們就需要集成規(guī)則引擎對這些判斷進(jìn)行線上化的管理

二、規(guī)則引擎選型

目前開源的規(guī)則引擎也比較多,根據(jù)原有項目依賴以及短暫接觸過的規(guī)則引擎,我們著重了解了一下幾個

drools:

-社區(qū)活躍,持續(xù)更新

-使用廣泛

-復(fù)雜

-學(xué)習(xí)成本較高

github.com/kiegroup/dr…

easy-rule:

-簡單易學(xué)

-滿足使用訴求

-長時間未發(fā)布新版

github.com/j-easy/easy…

easycode:

-京東物流同事維護(hù)的平臺

-基于flowable dmn實(shí)現(xiàn)

-配置簡單直觀

-已有大量系統(tǒng)使用

總結(jié):

  • 簡單配置型規(guī)則可以接入easycode,平臺提供配置頁面,通過jsf交互。
  • 復(fù)雜規(guī)則,需要動態(tài)生成規(guī)則,easycode目前還不支持。drools從流行度及活躍度考慮,都比easy-rule強(qiáng),所以選擇drools。

三、 drools簡單示例

3.1 引入依賴

<dependency>
    <groupId>org.kie</groupId>
    <artifactId>kie-spring</artifactId>
    <version>${drools.version}</version>
</dependency>

3.2 寫drl文件

我們寫一個簡單的demo

規(guī)則為:

匹配一個sku對象

0<價格<=100 10積分

100<價格<=1000 100積分

1000<價格<=10000 1000積分

在resources文件夾下新增 rules/skupoints.drl 文件 內(nèi)容如下

package com.example.droolsDemo
import com.example.droolsDemo.bean.Sku;

// 10積分
rule "10_points"
    when
        $p : Sku( price > 0 && price <= 100 )
    then
        $p.setPoints(10);
        System.out.println("Rule name is [" + drools.getRule().getName() + "]");
end

// 100積分
rule "100_points"
    when
        $p : Sku( price > 100 && price <= 1000 )
    then
        $p.setPoints(100);
        System.out.println("Rule name is [" + drools.getRule().getName() + "]");
end

// 1000積分
rule "1000_points"
    when
        $p : Sku( price > 1000 && price <= 10000 )
    then
        $p.setPoints(1000);
        System.out.println("Rule name is [" + drools.getRule().getName() + "]");
end

3.3 使用起來

    @Test
    public void testOneSku() {
        Resource resource = ResourceFactory.newClassPathResource("rules/skupoints.drl");
        KieHelper kieHelper = new KieHelper();
        kieHelper.addResource(resource);
        KieBase kieBase = kieHelper.build();
        KieSession kieSession = kieBase.newKieSession();
        Sku sku1 = new Sku();
        sku1.setPrice(10);
        kieSession.insert(sku1);
        int allRules = kieSession.fireAllRules();
        kieSession.dispose();
        System.out.println("sku1:" + JSONObject.toJSONString(sku1));
        System.out.println("allRules:" + allRules);
    }

    @Test
    public void testOneSku2() {
        Resource resource = ResourceFactory.newClassPathResource("rules/skupoints.drl");
        KieHelper kieHelper = new KieHelper();
        kieHelper.addResource(resource);
        KieBase kieBase = kieHelper.build();
        StatelessKieSession statelessKieSession = kieBase.newStatelessKieSession();
        Sku sku1 = new Sku();
        sku1.setPrice(10);
        statelessKieSession.execute(sku1);
        System.out.println("sku1:" + JSONObject.toJSONString(sku1));
    }

3.4 輸出

3.5 總結(jié)

如上,我們簡單使用drools,僅需要注意drl文件語法。根據(jù)drl文件生成規(guī)則的工作內(nèi)存,通過KieSession或者StatelessKieSession與工作內(nèi)存交互。整個流程并不復(fù)雜。注意 KieHelper僅是在演示中簡單使用,demo中包含使用bean來管理容器的方式,即便在簡單使用場景也不應(yīng)通過 KieHelper來重復(fù)加載規(guī)則。

但是,這樣并不能滿足我們線上化判斷,或者頻繁更改規(guī)則的訴求。于是我們在實(shí)踐中需要對drools更高階的使用方式。

四、 drools動態(tài)化實(shí)踐

從以上簡單demo中我們可以看出,規(guī)則依賴drl文件存在。而業(yè)務(wù)實(shí)際使用中,需要動態(tài)對規(guī)則進(jìn)行修改,無法直接使用drl文件。

以下是我了解過的四種動態(tài)的方案:

  • drt文件,創(chuàng)建模板,動態(tài)生成drl文件,也是我們目前所用的方式。
  • excel文件導(dǎo)入,實(shí)際上和模板文件類似,依然無法直接交給業(yè)務(wù)人員來使用。
  • 自己拼裝String,動態(tài)生成drl文件,網(wǎng)上大多數(shù)博文使用方式,過于原始。
  • api方式,drools的api方式復(fù)雜,使用需要對drl文件有足夠的了解。

最后介紹以下drools在項目中的實(shí)際使用方式

4.1 配置規(guī)則

我們的業(yè)務(wù)場景可以理解為多個緩沖池構(gòu)成的一個網(wǎng)狀結(jié)構(gòu)。

示例如下:

上圖中每個方塊為一個緩沖池,每條連線為一條從A緩沖池流向B緩沖池的規(guī)則。實(shí)際場景中緩沖池有數(shù)百個,絕大多數(shù)都有自己的規(guī)則,這些規(guī)則構(gòu)成一張復(fù)雜的網(wǎng)絡(luò)?;跇I(yè)務(wù)訴求,緩沖池的流向規(guī)則需要經(jīng)常變動,我們需要在業(yè)務(wù)中能動態(tài)改變這些連線的條件,或者改變連線。在這種情況下,如果使用靜態(tài)的drl文件來實(shí)現(xiàn)這些規(guī)則,需要數(shù)百規(guī)則文件,維護(hù)量大,且每次修改后使規(guī)則生效的代價較大。在此背景下,我們嘗試drools高階應(yīng)用,既規(guī)則動態(tài)化實(shí)踐。

我們在創(chuàng)建緩沖池的頁面中加入了流向規(guī)則的創(chuàng)建環(huán)節(jié)。每個緩沖池維護(hù)自己的流向規(guī)則,即為自己的一根連線。如下圖:

4.2 動態(tài)生成drl

drt文件內(nèi)容:

(實(shí)際業(yè)務(wù)模板中比這個復(fù)雜,有一定校驗及業(yè)務(wù)邏輯,此處做了簡化)

template header
// 模板需要使用的參數(shù)
id
cluePoolId
sourceList
cooperateTypeList
regionId
secondDepartmentId
battleId
outCluePoolId
amountCompareFlag
amount
salience

package rulePoolOut
// 全局對象
global java.util.List list;
global java.util.List stopIdList;
global java.util.List ruleIdList;
// 引入的java類
import com.example.drools.bean.ClueModel
import org.springframework.util.CollectionUtils
import org.apache.commons.lang3.StringUtils;
import java.lang.Long
template "CluePoolOut"
// 規(guī)則名稱
rule "clue_pool_@{cluePoolId}_@{id}"
//  參數(shù) 標(biāo)識當(dāng)前的規(guī)則是否不允許多次循環(huán)執(zhí)行
no-loop true
//  參數(shù) 優(yōu)先級
salience @{salience}
//  參數(shù) 規(guī)則組 本組規(guī)則只能有一個生效 
activation-group "out_@{cluePoolId}"
// 匹配的LHS
when
    $clue:ClueModel(cluePoolId == @{cluePoolId})
    ClueModel(CollectionUtils.isEmpty(@{sourceList}) || source memberOf @{sourceList})
    ClueModel(CollectionUtils.isEmpty(@{cooperateTypeList}) || cooperateType memberOf @{cooperateTypeList})
    ClueModel(secondDepart == @{secondDepartmentId})
    ClueModel(regionNo == @{regionId})
    ClueModel(battleId == @{battleId})
    ClueModel(null != estimateOrderCount && (Long.valueOf(estimateOrderCount) @{amountCompareFlag} Long.valueOf(@{amount})))
// 如果配置要執(zhí)行的RHS 支持java語法
then
    ruleIdList.add(@{id});
    $clue.setCluePoolId(Long.valueOf(@{outCluePoolId}));
    list.add(@{outCluePoolId});
    update($clue);
    }
end
end template

生成drl內(nèi)容: 根據(jù)一個隊列及模板的路徑進(jìn)行drl內(nèi)容的生成

List<CrmCluePoolDistributeRuleBusinessBattleVO> ruleCenterVOS = new ArrayList<>();
CrmCluePoolDistributeRuleBusinessBattleVO vo = new CrmCluePoolDistributeRuleBusinessBattleVO();
vo.setCooperateTypeList(Lists.newArrayList(1, 2, 4));
vo.setAmountCompareFlag(">");
vo.setAmount(100L);
ruleCenterVOS.add(vo);
String drl = droolsManager.createDrlByTemplate(ruleCenterVOS, "rules/CluePoolOutRuleTemplate.drt");


    public String createDrlByTemplate(Collection<?> objects, String path) {
        ObjectDataCompiler compiler = new ObjectDataCompiler();
        try (InputStream dis = ResourceFactory.newClassPathResource(path, this.getClass()).getInputStream()) {
            return compiler.compile(objects, dis);
        } catch (IOException e) {
            log.error("創(chuàng)建drl文件失敗!", e);
        }
        return null;
    }

4.3 加載drl

上邊的簡單示例中,我們使用了KieHelper 來加載規(guī)則文件至工作內(nèi)存中。實(shí)際上我們不可能在每次匹配中重新加載所有規(guī)則文件,所以我們可以單例的使用規(guī)則容器,通過以下方式或者也可以使用@Bean等方式來管理容器。

private final KieServices kieServices = KieServices.get();
// kie文件系統(tǒng),需要緩存,如果每次添加規(guī)則都是重新new一個的話,則可能出現(xiàn)問題。即之前加到文件系統(tǒng)中的規(guī)則沒有了
private final KieFileSystem kieFileSystem = kieServices.newKieFileSystem();
// 需要全局唯一一個
private KieContainer kieContainer;

通過將內(nèi)容寫入 kieFileSystem然后重新加載整個 kieBase即可重新加載規(guī)則,但是這種行為比較重,代價較大

也可以通過 kieBase新增一個文件來進(jìn)行加載,代價小,但是同步各個實(shí)例的代價較大。

KnowledgeBaseImpl kieBase = (KnowledgeBaseImpl)kieContainer.getKieBase(kieBaseName);
KnowledgeBuilder builder = KnowledgeBuilderFactory.newKnowledgeBuilder();
Resource resource = ResourceFactory.newReaderResource(new StringReader(ruleContent));
builder.add(resource,ResourceType.DRL);
if (builder.hasErrors()) {
    throw new RuntimeException("增加規(guī)則失敗!" + builder.getErrors().toString());
}
kieBase.addPackages(builder.getKnowledgePackages());

4.4 匹配

通過 StatelessKieSession與規(guī)則引擎交互

// 獲取一個鏈接
StatelessKieSession kieSession = droolsManager.getStatelessKieSession(RuleTemplateEnum.CLUE_POOL_OUT_RULE.getKieBaseName());
// 創(chuàng)建全局變量對象
List<Long> list = new ArrayList<>();
List<Long> stopIdList = Lists.newArrayList();
List<String> result = new ArrayList<>();
List<Long> ruleIdList = new ArrayList<>();
// 塞入全局變量
kieSession.setGlobal("ruleIdList", ruleIdList);
kieSession.setGlobal("list", list);
kieSession.setGlobal("stopIdList", stopIdList);
kieSession.setGlobal("result", result);
// 執(zhí)行規(guī)則
kieSession.execute(clueModel);

如果使用 KieSession則需要在使用完成后進(jìn)行關(guān)閉

kieSession.insert(clueModel);
kieSession.fireAllRules();
kieSession.dispose();

在執(zhí)行規(guī)則的過程中可以加入各種監(jiān)聽器對過程中各種變化進(jìn)行監(jiān)聽。篇幅原因交給各位去探索。

五、 總結(jié)

從上邊的流程中我們體驗了動態(tài)規(guī)則的創(chuàng)建以及使用。動態(tài)規(guī)則滿足了我們規(guī)則動態(tài)變化,規(guī)則統(tǒng)一管理的訴求。

我也總結(jié)了在這種使用方式下drools的幾個優(yōu)缺點(diǎn)。

優(yōu)點(diǎn):

  • 規(guī)則動態(tài)化方便
  • 在工作內(nèi)存中匹配規(guī)則性能好
  • 幾乎可以滿足所有的規(guī)則需求
  • 內(nèi)置方法豐富完善

缺點(diǎn):

  • 分布式一致性需要自行處理
  • 需要研發(fā)了解drl語法
  • 學(xué)習(xí)曲線陡峭
  • 匹配過程監(jiān)控手段需要自行實(shí)現(xiàn)

以上就是drools規(guī)則動態(tài)化實(shí)踐解析的詳細(xì)內(nèi)容,更多關(guān)于drools規(guī)則動態(tài)化的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

最新評論