以前两篇文章是去年调研和自研规则引擎的存货,今天是最后一篇,后记。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存储。 规则配置采用多租户管理,便于不一样团队进行操做。
日志存储
监控告警
对于使用流程进行埋点监控,可视化报表查看。
接入方式
核心功能