規則引擎調研與思考(一)

1. 規則引擎簡述

世界萬事萬物皆有規則java

提及規則引擎, 相信不少小夥伴對於規則引擎產生了不少疑問. 它是什麼? 它能作啥? 應該怎麼作? 但願經過閱讀下面的內容能給你一些啓發.git

首先規則引擎是什麼,咱們來看下百度百科是怎麼定義的github

規則引擎由推理引擎發展而來,是一種嵌入在應用程序中的組件,實現了將業務決策從應用程序代碼中分離出來,並使用預約義的語義模塊編寫業務決策。接受數據輸入,解釋業務規則,並根據業務規則作出業務決策。golang

上面說的很清晰, 總結一句話規則引擎作的事情就是 錄入特定判斷邏輯, 經過輸入的數據進行決策api

規則引擎這麼好? 咱們的業務適合引入規則引擎嗎?

首先咱們有個基本的優缺點分析:markdown

規則引擎帶來的優勢:架構

  1. 高靈活性併發

    高靈活性帶來的直接效果是縮短開發到上線週期, 熱更新修復bugide

  2. 天生組件化函數

    簡化複雜的業務邏輯, 縮減業務代碼量, 易於業務邏輯管理.

規則引擎帶來的缺點:

  1. 引入了額外服務依賴

    對於一些對穩定性、正確性要求極高的場景, 前期不建議引入 (須要提供完善的權限控制和規則單元測試能力)

  2. 前期增長產品、技術學習成本

    產品須要具備必定抽象思惟, 需求文檔中給出系統易變部分進行抽象處理

    研發須要學習部分規則語法, 並瞭解系統實現和約束

  3. 並不能依賴規則熱更新知足全部業務斷定場景

因此規則引擎並非萬能, 在熟悉規則引擎的具體能力前提下, 根據具體所在的業務場景, 來判斷引入後是否可達到效益最高

2. 規則引擎選擇

規則引擎/指標 drools gengine
上手難度 有必定門檻
運行方式 僅支持順序型 支持順序/並行/N-M等模式
開發語言 java golang
社區活躍度 通常

基於本人水平, 此處選擇了更易學習的 gengine 做爲研究對象 (雖然規則引擎有不一樣的運行模式和語言, 但對於咱們理解本質並沒什麼區別)

  • gengine是一款基於golang和AST(抽象語法樹)開發的規則引擎, gengine支持的語法是一種自定義的DSL
  • gengine於2020年7月由嗶哩嗶哩(bilibili.com)受權開源
  • gengine現已應用於B站風控系統、流量投放系統、AB測試、推薦平臺系統等多個業務場景
  • 你也能夠將gengine應用於golang應用的任何須要規則或指標支持的業務場景

gengine 規則引擎使用流程

支持部分規則模式

運行模式 方法名 含義
順序型 ExecuteSelectedRulesWithControlAndStopTag 按規則優先級執行(從上往下)-耗時爲全部規則執行時間累加
併發型 ExecuteSelectedRulesConcurrent 全部規則併發執行(執行時間爲執行時間最長的-考慮池限制)
混合模式 ExecuteSelectedRulesMixModel 先執行一個優先級最高的規則,而後併發執行剩下的全部規則
N-M ExecuteNConcurrentMConcurrent 前N個規則併發執行, M個規則也併發執行/ 前N個規則順序執行, M個規則併發執行
  1. 制定規則中依賴的方法(硬編碼)

    type ruleResponse struct {
      Code int
      Data map[string]interface{}
    }
    
    func success(code int) ruleResponse {
      return ruleResponse{
        Code: code,
        Data: make(map[string]interface{}),
      }
    }
    
    func (rule ruleResponse) AddDataParam(name string, value interface{}) {
      rule.Data[name] = value
    }
    複製代碼

    規則中是不支持自定義函數和結構體的, 當須要返回非int、字符串、bool值的時候, 須要咱們外部注入對應實現方法, 供規則內調用 (例如自定義結構體、自定義複雜規則驗證、獲取訂單課程等方法)

  2. 聲明規則

    rule1 := ` rule "case0" "case1測試用例" salience 0 //後執行 begin Print(flag-2) successObj := success(0) successObj.AddDataParam("name0","test 000 value") return successObj end rule "case1" "case1測試用例" salience 1 //後執行 begin a = 8 if a < 1 { println("a < 1") } else if a >= 1 && a <6 { println("a >= 1 && a <6") } else if a >= 6 && a < 7 { println("a >= 6 && a < 7") } else if a >= 7 && a < 10 { println("a >=7 && a < 10") } else { println("a > 10") } end rule "case2" "case2測試用例" salience 2 //先執行 begin if flag>0 { Print(flag+3) stag.StopTag = true successObj := success(222) successObj.AddDataParam("name2","test value") return successObj } end `
    複製代碼

    上圖中聲明瞭規則, Print、success是咱們外部注入的方法, 咱們將在下一步制定.

    rule表示一段新的規則開始 , 第一個爲規則名(返回結果時候爲對應key), 第二個爲描述, 第三個 salience 表示規則的優先級

    規則的優先級數字越大優先級越高(當使用 AsGivenSortedName 方法時, 會忽略掉規則內優先級做用 )

  3. 注入規則內依賴方法, 初始化規則池

    apis := make(map[string]interface{})
    apis["success"] = success
    apis["Print"] = fmt.Println
    
    var poolMinLen int64 = 50
    var poolMaxLen int64 = 100
    pool, err := engine.NewGenginePool(poolMinLen, poolMaxLen, engine.SortModel, rule1, apis)
    
    if err != nil {
     fmt.Println(err.Error())
     return
    }
    複製代碼

    第一個參數 poolMinLen 表示初始化池最小50

    第二個參數 poolMaxLen 表示初始化池最大100

    第三個參數爲設置執行模式(分爲順序執行、併發執行等), 只有調用 ExecuteSelectedRules 和 ExecuteSelectedRulesConcurrent 有效 (後續可作規則模式選擇)

    第四個參數是咱們配置的規則

    第五個是咱們注入的變量值、方法等

  4. 調用執行規則

    data := make(map[string]interface{})
    data["flag"] = 2
    
    stag := &engine.Stag{StopTag: false}
    data["stag"] = stag //顯示將標記注入進去
    
    //_ = pool.RemoveRules([]string{"case1"}) //移除規則
    /*_ = pool.UpdatePooledRulesIncremental(` //新增規則 rule "case66" "case66測試用例" salience 3 begin Print("case66") return false end `)*/
    
    _, resp := pool.ExecuteSelectedRulesWithControlAndStopTag(data, true, stag, []string{"case1", "case66", "case2", "case0"})
    fmt.Println(resp)
    //output:
    //5
    //map[case2:{222 map[name2:test value]}]
    
    複製代碼

    業務調用方變量注入, StopTag 聲明爲可中斷, 執行選定的規則

3. 業務系統應用

層級架構圖

image.png

業務場景經過業務編號(標識)調用規則接入層, 根據運行模式運行關聯的規則.

接入層負責根據業務編號拿到具體規則進行運行, 同時接入層負責收集業務執行的異常與結果

最底層規則信息管理部分, 負責規則信息數據的維護與整理

業務接入模塊分工流程

image.png

1、業務請求側流程步驟:

  1. 業務方請求 (參數爲 業務編號+業務數據) 執行規則
  2. 規則執行獲取業務編號對應的規則列表, 根據此業務的運行模式(順序型、並行等) 執行規則
  3. 執行完成後返回具體執行結果供業務使用

2、規則管理側能力:

  1. 規則錄入、修改、刪除
  2. 規則合併建立業務規則包, 生成業務編號供業務使用
  3. 運行模式/規範輸入輸出

4. 總結

隨着業務的快速發展, 代碼的生命週期愈來愈短, 項目慢慢發展成爲恐怖的「巨獸」, 吞噬着研發同窗的耐心, 叫苦連天卻難以破局, 上面講了這麼多, 咱們怎麼來判斷是否適合引入規則引擎呢?

首先達成共識, 領導及產研同事是承認當下值得去作的, 能夠解決掉咱們目前發現的痛點 (發現痛點能夠先經過 最簡單的方式先找出那些相似 if else多分支決策場景, 根據歷史改動及業務需求來進行判斷)

固然最好的話, 你對代碼所服務的業務具備很好的預見能力.

參考資料

www.bstek.com/#videos

rencalo770.github.io/gengine_doc…

github.com/bilibili/ge…

相關文章
相關標籤/搜索