ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Drule2.0规则引擎从入门到排坑:配置、会话与生产实践

Drule2.0规则引擎从入门到排坑:配置、会话与生产实践 接手Drule2.0这套规则引擎的时候我心里其实有点嘀咕。项目从1.x升级过来旧的规则文件全得重写接口层面也动了不少。当时团队里没人系统整理过新版的用法文档东一块西一块大多是一句“按老版本兼容”就带过了。结果上线测试第一周我们就被规则不生效、会话状态串掉这类问题连续锤了好几次。后来我花了两周时间把Drule2.0从配置、开发到上线排查的整套链路重新捋了一遍也把生产环境踩过的坑都补进了内部使用手册。这篇就当是一个更详细的外部版给正在或者准备用Drule2.0的同行一个参考。内容按照我对这套引擎的理解来组织重点放在“怎么用对”和“怎么排查错”上适合后端开发、规则配置人员以及对规则引擎选型感兴趣的同学。1. 规则引擎到底解决了什么问题先对齐一个认知很多团队把规则引擎当成“if-else的替代品”这么理解不能说错但容易用歪。以我参与的这个金融风控项目为例最直接的痛点不是代码写不出判断逻辑而是业务规则天天变下午三点业务方提需求说“贷款额度超过五万且历史逾期超过两次的订单要人工审核”第二天上午就要上线。传统的做法是改代码、发版、走回归一套流程下来最快也得一两天而且改一次代码就要动一次测试用例成本哗哗往上涨。Drule2.0把“决策逻辑”从业务代码里拆了出来变成了独立的规则文件。规则文件可以由业务分析师或运营人员通过配置界面维护也可以由开发以文本形式提交到仓库再走CI流程发布。推送一次规则相当于业务逻辑的即时热更新不需要重启服务也不需要重新编译主程序。再说深一层规则引擎的价值不只是“快”还有一个容易被忽略的点可审计。金融项目做风控决策需要回答“这笔订单为什么被拒”这个问题。如果判断逻辑散落在代码的十几个if-else里你很难快速给出完整依据。规则引擎里每一条规则都有独立的命名、条件描述和动作定义引擎执行完还可以输出详细的决策轨迹我在第7章会专门讲这个问题。Drule2.0和典型的Drools等老牌引擎相比最明显的差异是降低了规则编写的门槛同时把“规则集”和“会话生命周期”这两个概念彻底分开了。这意味着同一条规则可以被不同业务场景以不同参数加载执行也允许引擎在同一进程里跑多个相互隔离的规则会话。这个设计对于中大型项目来说很关键因为同一个风控服务可能同时服务好几个产品线每条产品线的规则模板和优先级策略并不相同。如果只是几十条固定判断说实话硬编码在代码里并不丢人。规则引擎的优势要等规则量上了百、变更频率上了周、参与维护的人超过两三个之后才会明显体现。所以我给你的第一条建议是先别急着给项目上规则引擎先盘一下规则的规模和变更频率。2. 六个核心概念从规则文件到会话一次讲透Drule2.0的官方文档里概念很多但真正贯穿日常开发的其实就六个。把这六个概念和它们之间的关系搞明白后面所有配置和接口调用都不会跑偏。2.1 事实引擎操作的对象事实是进入规则引擎参与匹配的数据对象。它可以是一个订单对象、一个用户实体也可以是一个Map。引擎本身不关心事实的业务语义只把它当作“是什么类、有哪些属性、当前值是多少”的数据载体。在Drule2.0的Java客户端里事实通常是POJO规则里通过全限定类名加属性引用来取值。比如规则文件里写OrderInfo(amount 50000)就是在匹配一个名为OrderInfo的事实对象并且要求它的amount字段大于等于50000。有一点要注意同一个会话里可以插入多个不同类型的事实引擎在处理时会把这些事实统一放进工作内存规则的条件部分可以引用其中任意一个或多个。2.2 条件与动作规则文件的一左一右规则文件里最核心的两个部分是条件Left Hand Side简称LHS和动作Right Hand Side简称RHS。条件描述“什么时候触发”动作描述“触发后干什么”。条件部分通常由字段约束、逻辑组合和事实之间的关联组成动作部分可以是修改变量值、调用日志、对外发消息或者只是简单地把规则名记下来。我自己写规则时的一个习惯是尽量让动作简单可控不要在动作里写太复杂的处理逻辑。规则引擎适合做决策不适合做重型业务处理。如果某个动作需要调用第三方服务或者写数据库建议放到引擎外部去做规则里只输出一个“建议结果”这样可以保持规则文件的轻量。2.3 规则集独立命名的规则容器规则集是一个逻辑容器把一组规则打包在一起。Drule2.0里一个规则文件或者一个发布包通常对应一个规则集。规则集之间是隔离的这意味着你创建会话时只能加载指定的一个或几个规则集不会互相干扰。项目里如果你的规则被分成“反欺诈规则集”“额度审批规则集”“黑名单规则集”它们在加载时就是独立、隔离的。这带来一个很大的好处你可以单独测试、单独发布某一个规则集而不影响其他正在运行的规则。2.4 无状态会话与有状态会话别选错这是Drule2.0实际使用中最容易出问题的地方也是文档里描述最抽象的地方之一。无状态会话就像一次性的纯净水杯你把事实丢进去引擎当场算完返回结果会话直接销毁。适合一次请求一次决策的场景比如“判断这个订单要不要风控拦截”。有状态会话则像一个持续对话的窗口你可以在会话里分多次插入事实每次插入后触发规则还可以在后续查询之前规则的执行结果。适合需要分阶段决策、或者在一个流程里多次依赖上一次结果的场景。我强烈建议除非你的业务流程确实需要跨多次触发保存中间状态否则优先用无状态会话。有状态会话用不好最典型的问题就是“上次请求的数据污染了本次结果”我在第7章会详细讲一个因为复用有状态会话导致的线上事故。2.5 规则模板与动态参数前面说过Drule2.0降低编写门槛主要体现在规则模板机制上。你可以在规则文件里定义模板变量比如minAmount和maxAmount然后在创建会话时传入参数值。同一个规则集因为传入参数不同可以形成“消费贷额度偏好模板”和“小微企业贷额度偏好模板”两套执行效果。这比旧版本里为每个商户复制一份规则文件的做法干净多了。以前我们给商户A写一份规则、给商户B复制一份再改改参数结果规则数量指数级膨胀维护成本高到想骂人。用模板参数后一份规则同时服务几十个不同配置的对象规则文件本身几乎不需要重复维护。2.6 规则函数的扩展方式Drule2.0不可能覆盖所有业务场景的自定义逻辑。文件里还预留了自定义函数的注册接口开发人员可以写一小段Java代码把判断逻辑打包注册成函数规则里直接调用。比如根据身份证号计算年龄段、根据经纬度判断是否属于同一城市这些逻辑就不是规则语言能优雅表达的注册成函数后在规则文件里用一行调用就行。这里有一个边界要守好自定义函数应该是“判断工具”不要用自定义函数去操作其他事实对象或者修改全局变量否则会让规则执行的可预测性变得很差。3. 规则文件长什么样一份风格灵活但边界清晰的配置Drule2.0的规则文件在社区版里支持好几种格式不同行业使用习惯也不同。我基于实际项目经验把规则文件拆解成两层来看。3.1 规则文件的基本骨架下面这段是个风格类似DRL但做了简化脱敏的示例用来展示核心字段的逻辑关系。Drule2.0对规则文件有一套schema校验规则如果使用了不属于本版本的关键字发布包在校验阶段就会报错。具体的文件名后缀和packaging方式建议以你所用发行版的官方CLI或编辑器插件提示为准。package demo.risk import com.example.dto.OrderInfo import com.example.dto.UserProfile rule 高危订单拦截 when $order : OrderInfo(amount 50000, channel ONLINE) $user : UserProfile(riskLevel HIGH) then $order.setIntercept(true); $order.setReasonCode(AMOUNT_HIGH_WITH_RISK); drule.log(hit rule: HIGH_RISK_ORDER); end几个关键点拆开说package表示所属包名类似Java的包主要用于组织和隔离规则。import引入需要操作的事实类。不引入直接用字符串类名也可以但会牺牲编译期检查线上跑出来容易出类型不匹配。rule 名称规则名称在当前规则集里必须唯一。一旦重复发布时直接报错。我曾经见过两个同事各自加了同名规则结果发布校验阶段卡了好久。when条件区。上面示例里两个模式之间默认是“同时满足”的关系。Drule2.0默认所有条件都是与的关系如果你想表达“或”需要显式使用or关键字或者拆成两条规则。then动作区。用类似Java的语法修改变量、打日志。注意变量上的$前缀只是惯例方便区分规则变量和事实对象的内部字段不是强制的。3.2 JSON风格规则描述文件业务配置团队更习惯的是JSON格式的规则描述。Drule2.0支持把规则拆成“描述动作脚本”的结构。下面是我们在某个信贷审批场景里用过的简化结构{ ruleSet: loan_check, ruleList: [ { ruleName: loan_overdue_check, description: 历史逾期超过3次且当前未结清的单直接拒绝, priority: 10, enabled: true, condition: { type: and, items: [ { field: overdueCount, operator: , value: 3 }, { field: currentStatus, operator: , value: UNPAID } ] }, action: { type: script, script: order.setResult(REJECT); order.setReason(OVERDUE); } } ] }JSON格式对非开发人员相对友好而且天然支持模板变量替换。配置人员不需要理解Java语法只需要知道字段名、比较符和结果集就能自己维护一套规则。我们把字段清单维护成一份数据字典上面的overdueCount、currentStatus就是字典里的标准字段规则配置人员照着字典写基本不会出界。3.3 条件操作符的种类和用法Drule2.0常用操作符大概是这些操作符示例含义说明status CLOSED等于字符串用双引号amount 10000数值比较age 25数值比较和上面一样支持整数与小数!status ! BLOCKED不等于inchannel in [APP, MINIAPP]属于给定集合not inlevel not in [LOW, MEDIUM]不属于给定集合matchesmobile matches ^1[3-9]正则匹配containstagList contains VIP集合字段包含某元素exists/not existsexists(SubOrder)工作内存中是否存在某类事实实际项目中遇得最多的是两种问题一是字符串比较漏了引号导致当成字段名解析二是时间类型的比较没有严格统一格式导致规则执行结果前后不一致。时间字段我建议统一转成时间戳long类型再比较不要直接比较日期字符串。3.4 动作区支持的不只是赋值动作区看起来像Java但它主要做三类事情给事实对象的字段赋值比如order.setResult(REJECT)。调用规则引擎内置API比如drule.log()输出执行日志或者用drule.insert()往工作内存里插入一个新事实从而触发后续规则。调用注册好的自定义函数比如if (riskUtil.isBlack(mobile)) { ... }。动作区不适合做的是长时间阻塞的IO操作、复杂事务、需要异步回调的流程。遇到这些需求更合理的做法是规则只负责设置一个标记值真正的处理逻辑放到引擎外部通过监听器或者后置钩子去完成。4. 引擎内部是怎么找到那条规则的优先级、冲突与规则链用规则引擎的人经常有一个疑问“我插入了事实引擎到底按什么顺序执行规则如果好几条规则都能匹配上先执行谁”这一章把顺序问题讲透。Drule2.0的处理机制并不神秘它本质上做的是模式匹配匹配完成之后再进行规则冲突的解决与激活执行。4.1 规则执行的两阶段流程第一阶段是“匹配”。事实插入工作内存后引擎会把事实对象和当前规则集里的所有规则条件进行匹配。注意不是顺序遍历那种简单匹配引擎内部有一个索引结构来缓存模式与事实之间的关联这也是为什么大量规则下引擎的执行性能依然能稳住的原因。这个阶段的结果是产生一个“激活表”也就是“哪些规则在当前事实组合下被满足”。第二阶段是“执行-冲突解决”。如果激活表有多条激活的规则引擎按照议程Agenda的次序逐条执行动作区的代码。一条规则动作执行完毕后如果它向工作内存插入了新事实或者修改了已有事实引擎会重新进行匹配产生新一轮激活继续执行直到没有新激活规则为止。4.2 优先级是怎么排的数字越大越先跑Drule2.0里的规则优先级用priority字段控制默认值是0。数值越高在当前议程里越靠前执行。规则文件里可以写成rule 高优先级规则 priority 100 when ...在JSON风格里对应priority: 100。我在配置优先级时踩过一个教训不要指望同一事实下互不相关的规则也严格按照优先级排顺序。优先级只在“匹配同一组事实的规则”之间的执行顺序上有意义如果规则触发的条件本质上不同那么它激活的时机都不一样硬调优先级反而不容易理解。把规则按业务阶段分层优先保证层内顺序正确跨层的顺序交给事实流转去控制是我目前觉得最清晰的做法。4.3 冲突解决的三条隐性规则如果优先级相同且都匹配了同一组事实Drule2.0内部按照一个固定的规则来排序虽然不同发行版可能略有差异但大致思路是规则的“特化程度”高者优先。也就是条件写得越具体、约束条件越多的规则系统认为它信息量更大越该先执行。事实插入顺序靠前的优先。先插入的事实匹配出来的激活规则排在前面。如果规则文件里显式声明了规则顺序按声明的先后关系执行。实际配置中不要指望这些隐性排序来解决业务问题而是要对“同时命中且互相影响”的规则设置显式的优先级防止隐性排序变化时出现行为抖动。4.4 规则链与重复触发保护规则动作里可以插入新事实并触发后续规则这形成了规则链。规则链可以简化复杂决策流程的表达但有一个危险如果规则A的动作为事实X设置了一个值而规则B的条件又依赖这个值等规则B执行完后又反过来修改了事实X导致规则A再次激活就可能无限循环。Drule2.0在循环防护上提供了开关配置同时建议规则作者在容易形成环的地方设置触发次数上限这类业务条件。遇到线上规则无限循环问题先看日志里有没有同一个规则名反复出现再检查动作区是否改动了条件区依赖的字段。关于规则链还有一点非常重要如果一条规则的触发条件里依赖了自己动作修改过的字段最好仔细测试否则很容易出现“你以为只跑一次实际被再次激活”的情况。除非确有需要否则条件字段和动作修改字段应该尽量错开。4.5 决策表Excel也能做规则Drule2.0的配置中心支持把规则以决策表形式导入。决策表比较适合“条件多、结果为离散值”的场景第一行放条件字段下一行放比较符第三行放具体条件值最后一行放结果。决策表在后台会被引擎编译成规则文件。业务团队非常喜欢这种方式因为它和Excel筛选的操作习惯一致。但决策表不擅长的是处理复杂逻辑组合比如多表关联判断还是老老实实用规则文件更稳妥。5. 从搭建到跑通第一次请求集成流程和关键步骤这章直接上实操。我用JavaKotlin混合的微服务项目环境来演示不过Drule2.0本身也提供了其他语言的客户端思路是一样的。建议先在一个干净的Spring Boot工程里跑通最小链路再对接现有业务。5.1 工程依赖与初始化项目构建工具如果是Maven第一个动作是引入Drule2.0的客户端依赖以及对应的规则包解析器。依赖配置的具体groupId和版本号跟随你的发行版走公司内部如果使用私有仓库以私服坐标为准。引入依赖后建立一个配置类把规则集发布到本地缓存的路径配好。Configuration public class DruleConfig { Bean public RuleEngineService ruleEngineService() { RuleEngineOptions options RuleEngineOptions.builder() .ruleBasePath(/opt/rules) .enableCompileCache(true) .build(); return new RuleEngineService(options); } }这里面的关键点是ruleBasePath指向存放规则文件或发布包的目录。Drule2.0启动时会扫描这个目录下的规则包并预编译所以规则文件的改动虽然可以不重启服务但发布到该目录的时机需要和你的文件同步更新机制配合好。5.2 创建无状态会话并执行一次决策首次跑通最推荐的方式是写一个单元测试直接创建会话、插入事实、触发执行、断言结果。Test void testHighAmountOrderShouldBeIntercepted() { RuleEngineService service ruleEngineService(); RuleSession session service.newSession(risk_control_rule_set); OrderInfo order new OrderInfo(); order.setAmount(80000); order.setChannel(ONLINE); session.insert(order); session.fireAllRules(); assertTrue(order.isIntercept()); assertEquals(AMOUNT_HIGH, order.getReasonCode()); }分步骤解释newSession(risk_control_rule_set)根据规则集名称创建一个无状态会话。session.insert(order)把订单对象当作事实插入工作内存。session.fireAllRules()执行所有匹配规则的激活项。动作区通过order.setIntercept(true)直接修改了传入对象。由于Java对象引用传递主线程的order对象在fire之后已经发生了变化所以后面可以直接断言。这是最简单也最稳妥的使用方式。事实对象会被直接修改因此要特别注意不要在同一个请求里对同一个对象开多个会话操作否则可能出现并发覆盖。5.3 创建有状态会话并分阶段触发有状态会话适合依赖“上一次规则执行结果”的场景。比如先走一次反欺诈校验结果没问题再走额度审批。示例RuleSession session service.newSession(credit_flow_rule_set); OrderInfo order new OrderInfo(); order.setAmount(30000); UserProfile profile new UserProfile(); profile.setRiskLevel(MEDIUM); session.insert(order); session.fireAllRules(); session.insert(profile); session.fireAllRules(); OrderDecision decision session.getGlobal(decision);有状态会话持有了工作内存中的事实所以能在多个阶段之间保留状态。它的问题是如果整个会话执行过程中发生异常会话里所有中间状态都必须主动清理否则下一次复用就会受影响。上面代码里没有做关闭严格的写法应该在finally块里调用session.dispose()释放会话或者干脆每次请求都创建新会话。如果你用的是有状态会话并且容器是线程池的一定要想清楚“这个会话是否会被多个线程共用”。无状态会话没有这个问题因为它每次请求都新建并销毁。5.4 监听器和审计日志配置Drule2.0提供事件监听机制可以在规则命中前后、议题激活前后挂钩子实现审计日志收集、指标量统计等功能。实现接口后注册进会话即可。正常情况下线上会记录以下内容请求唯一ID、进入引擎时间、命中的规则名列表、每条规则的动作结果摘要、总耗时。这些审计数据是从“结果”回溯到“原因”的钥匙建议一定在早期就配上不然后面出问题排查成本很高。5.5 一个常见的跑通后问题很多第一次接入Drule2.0的团队会困惑为什么我在配置中心改了规则测试数据也变了但线上执行的还是老结果排查方向通常是缓存没刷新或者新旧版本规则集名一样导致发布包被覆盖。Drule2.0的规则编译缓存是按规则集名版本号做的如果你的发布包没有显式改变版本号规则集会继续沿用缓存版本。修改规则并发布时建议带一个递增版本号同时确认配置中心的缓存刷新策略是否覆盖了所有实例节点。6. 几个容易忽略的配置细节会话隔离、缓存与规则数量控制规则引擎用久了你会发现真正让你头疼的往往不是规则写得不对而是引擎在特定配置下的表现不符合直觉。这一章讲三个值得提前做好的配置决策。6.1 会话隔离级别怎么选Drule2.0本身支持多规则集隔离但同一个规则集能不能被多个会话安全并发执行取决于会话的隔离级别。默认来说无状态会话每次新建相对安全。如果追求性能优化想用会话池复用对象一定要确认会话里是否有全局变量。我见过一个案例规则动作区往全局变量里存了用户ID会话池复用后第二个用户跑同一条规则时读到的全局变量还是第一个用户的ID直接导致风控规则误判。后来我们把全局变量的使用全部禁止改为通过事实对象的字段传递才彻底解决。全局变量能不用就不用这是我在Drule2.0实战里学到的第一课。6.2 编译缓存的正确打开方式规则文件发布后第一次被请求时需要编译编译耗时可能到几百毫秒甚至更多。如果希望冷启动也体验良好可以开启异步预编译。Drule2.0支持在规则集发布时主动触发一次编译把产物放入本地缓存目录。配置缓存时要注意清理策略长期运行的服务如果规则包版本频繁升级本地缓存会留下大量历史版本的编译产物占用磁盘空间。建议文件保留数量和保留时间都做一个明确限制。6.3 规则数量多少算太多社区里总有声音问“Drule2.0能支撑多少条规则”我个人的经验是单规则集内规则数量控制在几百条级别是完全没有问题的引擎内部的索引机制能支持相对可观的规则量。真正带来性能麻烦的是“规则之间有大量交叉引用且触发链较长”的情况并不是规则条数本身。打个比方一万条彼此独立的规则就像一万个独立的检查关卡跑完也就挨个过一遍但十条互相触发的规则可能形成指数级循环把引擎卡死。所以做规则架构设计时与其纠结条数不如把每一条规则的边界划清楚尽量减少规则之间的隐式依赖。7. 生产环境踩坑实录四类高频问题的完整排查链路这章写的都是我亲眼见过甚至亲手踩过的坑每个项目的背景细节都做了脱敏处理但排查链路是完整的。希望你看完能少走一半弯路。7.1 规则“命中”了但动作没生效现象测试环境规则跑得好好的上线后日志里能看到命中的规则名但订单并没有被拦截结果值也没有按预期改写。排查链路首先检查动作区是否加了对字段非空的约束。我们遇到过的事实是动作区执行了order.setResult(REJECT)但后面的另一个动作分支又把result覆盖成了空值。打开完整审计日志看动作区代码块的执行顺序。因为同一组事实同时命中多条规则时执行顺序由优先级决定如果你在低优先级规则里做了覆盖高优先级规则的结果就会被冲掉。检查规则文件里是否有多个同名规则。同名冲突在发布时可能被拦截但旧版本规则集如果还残留在缓存里新规则命中后动作可能被旧规则的后续执行覆盖。最后结论是这个场景本质是优先级设计不合理多条规则共享同一个输出字段却没有约定唯一最终写者。修复方案是调整优先级并增加字段写入前的前置校验同时在测试环境增加了一个“同一字段多次写”的静态扫描。7.2 有状态会话复用时发生的用户数据串号现象两个用户并发请求A用户跑到某一步时突然出现B用户的用户名和订单金额被错误地打进了审计日志。排查链路第一反应怀疑缓存键设置错了。查完缓存没问题但发现服务里复用了同一个规则会话对象。查看会话创建代码发现为了性能优化之前人把newSession()结果放进了本地线程池的ThreadLocal里期望同一线程复用。问题是ThreadLocal并不能保证同一个用户的请求始终落在同一个线程。于是出现了A用户的会话事实还没清空B用户的请求就插入到同一会话的情况。临时方案是调高会话对象的使用周期把它从请求级改成业务级终态方案是把有状态会话改成无状态会话不保留跨请求状态。这是有状态会话最典型的坑一旦出现数据串号后果比规则不生效更严重。我的建议是除非业务有强需求否则不要为了解决性能问题去复用会话性能和正确性之间永远要优先正确性。7.3 时间比较出现跨天差异现象一条“超过当天18点不做自动审批”的规则晚上八点应该拦截但实际没有拦截。排查链路先检查规则条件里的时间字段类型。发现规则写的是currentTime 18:00:00字符串直接比较。不同规则节点所在服务器的时区一致但字符串格式在进入规则引擎前被某个环节转换成了带时区的日期时间导致比较结果和预期不符合。修复方式是把时间字段统一转成时间戳再比较同时在前置接口层把日期时间标准化规则文件里禁止直接比较日期字符串。这个问题同时提醒我们规则引擎的匹配结果严重依赖事实对象的值因此“进入引擎前的事实清洗”要和“规则条件设计”放在一起考虑。字段时区不统一、格式不统一的脏数据进入引擎后规则写得再对也可能得到错结果。7.4 规则循环导致引擎卡死现象某个订单触发了某条规则后进程CPU飙升请求一直不返回直到超时。排查链路查看引擎日志发现同一个规则名在短时间内被激活了上百次。打开该规则的动作区发现它修改了条件区依赖的一个字段条件满足后再次激活形成了自触发循环。查看规则文件历史这个规则原本是修改另一个辅助字段的后来一次重构把赋值对象改错了指向了条件区的判断字段。修复方向是把动作区和条件区完全解耦明确哪些字段是“输入字段”哪些字段是“输出字段”并用规则文件注释标明。循环触发看起来很吓人但其实规则引擎本身有防护机制如果你的版本没有自动打开循环保护建议在配置中心里打开。同时给规则集设置合理的执行超时时间就算真出现循环也能保证请求快速失败而不是拖垮整个服务。8. 规则引擎上线前的检查清单从测试到灰度最后这部分不是总结而是我们上线前用的内部清单你完全可以抄走做二次确认。规则编写阶段每条规则是否有清晰、唯一的命名说明字段里是否写了业务含义。条件区是否有可以提前合并的模式减少无意义的重复匹配。动作区是否直接或间接修改了条件区依赖的字段如有是否显式设计过循环策略。输入字段的数据类型是否统一字符串格式是否满足规则条件的格式要求。测试阶段是否准备了最小正向用例、最小反向用例和边界值用例。是否测试了字段为空、字段类型异常、对象为null的场景。是否验证了规则优先级与你预期一致而不是仅靠“跑通得到正确结果”来推断。是否在新规则集发布时对旧规则集做了回归对比。发布与灰度阶段规则集版本号是否递增。是否先在一台机器或少量流量上灰度。是否配置了规则执行的监控面板至少能看到命中率、平均耗时、异常规则名。是否部署了规则快速回滚能力。回滚不只是把规则集降级还要确认本地编译缓存的清理。从运维角度我还建议给规则引擎单独设置性能监控指标不要和应用主流程的指标混在一起。因为规则引擎的耗时波动往往能告诉你业务规则最近是不是出了变化把它独立出来定位问题会快很多。我个人在实际操作中的一个体会是Drule2.0本来就可以成为业务和工程之间的一层“通用语言”。让业务方用JSON格式维护基础规则让工程师把需要复杂逻辑的规则收口成自定义函数两者结合规则引擎在团队里才能真正用起来而不是变成一个谁都敬而远之的黑盒。希望这份偏实战的梳理能帮你省下我当初填坑的那些时间。
返回列表