以前兩篇文章是去年調研和自研規則引擎的存貨,今天是最後一篇,後記。json
有人會問,標題不是寫的動手擼嗎?哪裏體現擼了?緩存
其實擼起來一個引擎並不複雜,爲了體現架構思想,調研心得和設計思想反而更重要,相信優秀如你寫代碼沒有任何壓力的。架構
那我就和你們聊聊業務背景和引擎要求。app
好比[券表],對於字段屬性有必定的規則要求,好比券的互斥屬性須要作必定的校驗,好比change-config是個json,須要進行解析以後和detail信息作規則校驗,等其餘的一些規則。 梳理出來的須要主要設計到字段屬性的處理,而沒有涉及到複雜的流程,數據問題的處理。異步
硬編碼:適用於規則不易變場景。ide
優勢性能
缺點單元測試
Drools:開源規則引擎測試
流程:業務分析師編寫業務需求文檔,開發工程師根據規則進行DSL規則編寫,DSL規則入庫,Drools引擎根據規則庫規則進行解析,動態執行規則。優化
優勢
缺點
規則複雜以後,依然存在很差維護問題,某種程度上甚至比硬編碼糟糕。
過多的if,else,when,then不利於維護。
基於Spark數據處理規則引擎
若是場景涉及大部分規則是數據處理,則能夠認爲此場景規則處理等於數據處理。 爲商業分析師提供友好可視化規則界面。 規則引擎將配置信息解析爲Spark做業進行計算。
優勢
缺點
基本假設
n個規則輸入,一個規則結果輸出; 規則支持基本的邏輯運算,算數運算,關係運算,屬性判斷等; 多個原子語義規則之間可聚合,可複用,可拆分;
性能要求
可用性要求
規則引擎可降級,不影響主流程;
系統設計要求
功能要求
設計方案
技術點
難點
規則解析
示例
賦值
coupon.setId(100L); coupon.setAcctId(5L); coupon.setRemark("備註備註備註備註備註1"); coupon.setType((byte) 5); coupon.setDetail("{\"an\":\"\",\"charge_detail\":[{\"charge_amount\":100000,\"charge_side\":\"1\",\"order_no\":1,\"setAn\":false,\"setCharge_amount\":true,\"setCharge_side\":true,\"setOrder_no\":true},{\"an\":\"17D03004416N001S0009T187R00493A0080$C1CB4\",\"charge_amount\":20000,\"charge_side\":\"14010\",\"order_no\":2,\"setAn\":true,\"setCharge_amount\":true,\"setCharge_side\":true,\"setOrder_no\":true}],\"charge_detailIterator\":[{\"$ref\":\"$.charge_detail[0]\"},{\"$ref\":\"$.charge_detail[1]\"}],\"charge_detailSize\":2,\"charge_method\":0,\"charge_side\":\"1\",\"charge_type\":1,\"default_charge_side\":\"1\",\"default_side_an\":\"\",\"setAn\":true,\"setCharge_detail\":true,\"setCharge_method\":true,\"setCharge_side\":true,\"setCharge_type\":true,\"setDefault_charge_side\":true,\"setDefault_side_an\":true}"); coupon.setCount(120000L);
規則
rule.setKey("id"); rule.setRule("id > 10"); rule.setErrMessage("id 必須大於 10"); rule.setKey("type"); rule.setRule("acctId == 5 && type == 5"); rule.setErrMessage("acctId = 5 type 必須爲5"); rule.setKey("remark"); rule.setRule("string.length(' remark ') > 10"); // 注意表達式都有空格 rule.setErrMessage("remark長度不能小於10"); rule.setKey("detail"); rule.setRule("(<json>detail.charge_detail.(0).charge_amount</json> + <json>detail.charge_detail.(1).charge_amount</json>) == <json>count</json>"); rule.setRuleTypeEnum(CompassRuleTypeEnum.JSON); rule.setErrMessage("charge_amount值不相等");
Json
{ "an":"", "charge_detail":[ { "charge_amount":100000, "charge_side":"1", "order_no":1, "setAn":false, "setCharge_amount":true, "setCharge_side":true, "setOrder_no":true }, { "an":"17D03004416N001S0009T187R00493A0080$C1CB4", "charge_amount":20000, "charge_side":"14010", "order_no":2, "setAn":true, "setCharge_amount":true, "setCharge_side":true, "setOrder_no":true } ], "charge_detailIterator":[ { "$ref":"$.charge_detail[0]" }, { "$ref":"$.charge_detail[1]" } ], "charge_detailSize":2, "charge_method":0, "charge_side":"1", "charge_type":1, "default_charge_side":"1", "default_side_an":"", "setAn":true, "setCharge_detail":true, "setCharge_method":true, "setCharge_side":true, "setCharge_type":true, "setDefault_charge_side":true, "setDefault_side_an":true }
方案和核心代碼現起來比較簡單,可是一個非業務相關項目可能面對着兩個問題,作大和作小。
作小
指的是隻知足當前業務場景和需求,這樣可最快的實現需求,但後續若是有相似的需求不能知足或不易定製,這樣最開始引入這個項目的目的全無。
作大
指的是若是作成一個業務和技術上均可用的項目,達到一個平臺的效果,則須要在研發投入更多的時間。
包括代碼細節,技術方案,UI界面等,後續在系統穩定性方案也須要投入一些時間,這樣作一個非業務相關性的東西投入這麼大是否值得,好比須要大概投入了3,4我的力,完整週期持續了小2個月。
規則引擎有多個場景:風控場景,業務場景。
風控場景
屬於風控產品線產品,總體上功能比較完備,可是對於通常場景顯得重一些,引入了場景,規則,規則因子,appkey,單元測試等不少新的概念,總體上比較重。
業務場景
業務場景比較輕量級一些,對於咱們的場景支持的還能夠,引入上對於代碼入侵能夠接受。
決定採用這種低侵入方案進行對接。
調研收穫
進行調研以前,本身對於業務場景對於規則引擎的需求進行了必定的設計和代碼開發,在考慮上存在一些問題。
通用的方案
調研了一圈,你們你們在實現細節上都很相似,好比基於Aviator的表達式,經過Zk,MQ,DataBus的規則下發,規則放到內存中不存在跨進程調用。
離線方案基於Hive進行分析。
徹底避免代碼侵入 我本身設計的方案上想對代碼作到儘可能少的代碼入侵,甚至零侵入,好比經過AOP的方式實現零侵入。
但通過深度思考後,發現徹底的零侵入會限制規則在程序中的能力,調研了幾個方案以後,發現全部的方案都存在代碼侵入。
獲得的收穫是,不要爲了某種潔癖達到零侵入,適當的代碼侵入更有助於規則引擎的表達,只要作好侵入更友好就能夠。
規則沉澱
經過規則管理平臺配置和修改規則,基於MySql存儲。 規則配置採用多租戶管理,便於不一樣團隊進行操做。
日誌存儲
監控告警
對於使用流程進行埋點監控,可視化報表查看。
接入方式
核心功能