drools 如何動態加載規則

drools版本:7.0
語言:java8
描述:動態從數據庫中加載規則,並且加載到工作內存中

 

最終實現效果:從數據庫中讀取數據生成drl格式的字符串以後,可以一次性加載到工作內存中,也可以逐次加載到內存中進行build,而之前已經build好的規則不會消失。

下面是具體實現過程:

首先drools提供了常見的兩種加載規則的方式,一種是通過定義kmodule.xml的方式進行加載,並且在對應的package下寫好drl規則文件即可,xml文件如下,規則文件略。

 <?xml version="1.0" encoding="UTF-8"?>
    <kmodule xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xmlns="http://www.drools.org/xsd/kmodule">
    <kbase name="rule1KB" packages="rules">
        <ksession name="rule1KS"/>
    </kbase>
    </kmodule>

 

通過這種xml加載的方式在具體java代碼中獲取session會話的方式是這樣的:

KieContainer kc =KieServices.Factory.get().getKieClasspathContainer("rule1KB");
KieSession kieSession = kc.newKieSession("rule1KS");

 

注意獲取KieContainerKieSession 的名稱和xml中的對應。

但是在實際的動態加載中,drl格式的文件極有可能不是寫好了的,而是從數據庫中取出來數據,然後通過拼接字符串的方式動態生成的(至少我們現在這個項目就要這樣做(_))。
所以採用這種方式就有問題了,好在drools官方提供了另一種加載規則文件的方式,具體代碼如下:

 public KieContainer loadForRule(String drlStr) {
        KieServices ks = KieServices.Factory.get();
        KieRepository kr = ks.getRepository();
        KieFileSystem kfs = ks.newKieFileSystem();

        kfs.write("src/main/resources/rules/" + drlStr.hashCode() + ".drl", drlStr);
        
        // 將KieFileSystem加入到KieBuilder
        KieBuilder kb = ks.newKieBuilder(kfs);

        // 編譯此時的builder中所有的規則
        kb.buildAll();
        if (kb.getResults().hasMessages(Message.Level.ERROR)) {
            throw new RuntimeException("Build Errors:\n" + kb.getResults().toString());
        }

        return ks.newKieContainer(kr.getDefaultReleaseId());
    }

 

通過上面這段代碼實際上已經可以通過動態加載的方式拿到了一個KieContainer 類,繼續通過KieContainer 獲取到KieSession會話即可(實際上創建一次會話代價極低),具體一行代碼如下:

KieSession kieSession = kContainer.newKieSession();

但是通過上面這段代碼,每次都是重新加載,會出現這樣的問題:現在加載了A規則,再去加載B規則,A規則已經不存在與工作內存中了,這個問題簡單思考一下應該就是每次的Kie裏面的對象都是重新new的,所以每次都是重新保存,這明顯和實際要求不合,最直接想到的應該就是緩存一個KieContainer 方法,但是這種方式沒有成功,最後採用全局緩存了kieFileSystemkieRepository進行處理。具體代碼如下(省略了get/set方法):

public class KieUtils {
    private static KieContainer kieContainer;

    private static KieSession kieSession;

    private static KieServices kieServices;

    private static KieRepository kieRepository;

    private static KieFileSystem kieFileSystem;

    public static void initAndNotClear(){
        if (Objects.isNull(kieServices)) 
            kieServices = KieServices.Factory.get();

        if (Objects.isNull(kieRepository)) 
            kieRepository = kieServices.getRepository();

        if (Objects.isNull(kieFileSystem)) 
            kieFileSystem = kieServices.newKieFileSystem();
    }
}

 

使用上面這個類進行緩存,將上面的代碼獲取對應類的地方替換回去,比如:

KieUtils.initAndNotClear();
KieFileSystem kfs = KieUtils.getKieFileSystem();

對於這種方式的思考:
這種方式實際上還是buildKieFileSystem 中的所有的規則,實際上是一種假的添加新的規則進原規則體系中編譯的方式,但實際解決了問題,並且因爲動態生成最耗時間的地方在生成字符串,這種方式字符串是不用重新生成了,一般測試編譯所有規則的時間是以秒級單位的,並且RETE算法本身就是一種以空間換取時間的算法,只要在規則觸發時速度夠快,編譯時間稍微多耗一點,還是可以接受的。

以上爲轉載:https://www.jianshu.com/p/371d4a2a5866

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章