世界萬事萬物皆有規則java
提及規則引擎, 相信不少小夥伴對於規則引擎產生了不少疑問. 它是什麼? 它能作啥? 應該怎麼作? 但願經過閱讀下面的內容能給你一些啓發.git
首先規則引擎是什麼,咱們來看下百度百科是怎麼定義的github
規則引擎由推理引擎發展而來,是一種嵌入在應用程序中的組件,實現了將業務決策從應用程序代碼中分離出來,並使用預約義的語義模塊編寫業務決策。接受數據輸入,解釋業務規則,並根據業務規則作出業務決策。golang
上面說的很清晰, 總結一句話規則引擎作的事情就是 錄入特定判斷邏輯, 經過輸入的數據進行決策api
首先咱們有個基本的優缺點分析:markdown
規則引擎帶來的優勢:架構
高靈活性併發
高靈活性帶來的直接效果是縮短開發到上線週期, 熱更新修復bugide
天生組件化函數
簡化複雜的業務邏輯, 縮減業務代碼量, 易於業務邏輯管理.
規則引擎帶來的缺點:
引入了額外服務依賴
對於一些對穩定性、正確性要求極高的場景, 前期不建議引入 (須要提供完善的權限控制和規則單元測試能力)
前期增長產品、技術學習成本
產品須要具備必定抽象思惟, 需求文檔中給出系統易變部分進行抽象處理
研發須要學習部分規則語法, 並瞭解系統實現和約束
並不能依賴規則熱更新知足全部業務斷定場景
規則引擎/指標 | drools | gengine |
---|---|---|
上手難度 | 有必定門檻 | 易 |
運行方式 | 僅支持順序型 | 支持順序/並行/N-M等模式 |
開發語言 | java | golang |
社區活躍度 | 高 | 通常 |
基於本人水平, 此處選擇了更易學習的 gengine 做爲研究對象 (雖然規則引擎有不一樣的運行模式和語言, 但對於咱們理解本質並沒什麼區別)
支持部分規則模式
運行模式 | 方法名 | 含義 |
---|---|---|
順序型 | ExecuteSelectedRulesWithControlAndStopTag | 按規則優先級執行(從上往下)-耗時爲全部規則執行時間累加 |
併發型 | ExecuteSelectedRulesConcurrent | 全部規則併發執行(執行時間爲執行時間最長的-考慮池限制) |
混合模式 | ExecuteSelectedRulesMixModel | 先執行一個優先級最高的規則,而後併發執行剩下的全部規則 |
N-M | ExecuteNConcurrentMConcurrent | 前N個規則併發執行, M個規則也併發執行/ 前N個規則順序執行, M個規則併發執行 |
制定規則中依賴的方法(硬編碼)
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值的時候, 須要咱們外部注入對應實現方法, 供規則內調用 (例如自定義結構體、自定義複雜規則驗證、獲取訂單課程等方法)
聲明規則
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 方法時, 會忽略掉規則內優先級做用 )
注入規則內依賴方法, 初始化規則池
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 有效 (後續可作規則模式選擇)
第四個參數是咱們配置的規則
第五個是咱們注入的變量值、方法等
調用執行規則
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 聲明爲可中斷, 執行選定的規則
層級架構圖
業務場景經過業務編號(標識)調用規則接入層, 根據運行模式運行關聯的規則.
接入層負責根據業務編號拿到具體規則進行運行, 同時接入層負責收集業務執行的異常與結果
最底層規則信息管理部分, 負責規則信息數據的維護與整理
業務接入模塊分工流程
隨着業務的快速發展, 代碼的生命週期愈來愈短, 項目慢慢發展成爲恐怖的「巨獸」, 吞噬着研發同窗的耐心, 叫苦連天卻難以破局, 上面講了這麼多, 咱們怎麼來判斷是否適合引入規則引擎呢?
首先達成共識, 領導及產研同事是承認當下值得去作的, 能夠解決掉咱們目前發現的痛點 (發現痛點能夠先經過 最簡單的方式先找出那些相似 if else多分支決策場景, 根據歷史改動及業務需求來進行判斷)
固然最好的話, 你對代碼所服務的業務具備很好的預見能力.