策略引擎架构设计:整体架构分层、核心模块与数据流
做策略引擎这几年,我最大的感触是:架构设计决定了系统的上限。你想想看,规则越来越多,业务越来越复杂,如果一开始分层没做好,后面改起来简直要命。今天我就把策略引擎的架构拆开揉碎了讲给你听。
一、整体架构分层:三层模型
我个人习惯把策略引擎分成三层,每一层各司其职。说白了,就是「接入-计算-存储」的经典模式,但每一层都有策略引擎特有的设计考量。
| 层级 | 职责 | 关键组件 |
|---|---|---|
| 接入层 | 接收请求、参数校验、协议转换 | API 网关、请求适配器、限流器 |
| 决策层 | 规则解析、条件匹配、动作执行 | 规则解析器、执行引擎、决策缓存 |
| 数据层 | 规则存储、因子数据、日志持久化 | 规则仓库、因子库、审计日志 |
嗯,这里要注意:决策层是核心,也是我们今天重点讲的部分。接入层和数据层虽然重要,但很多通用中间件都能搞定,真正体现策略引擎功力的,是决策层那三个模块怎么配合。
二、核心模块拆解
1. 规则解析器:从文本到内存模型
规则解析器干的事,就是把人类写的规则文本,变成机器能理解的内存结构。我在项目中遇到过最头疼的事——业务方用 Excel 写了 2000 条规则,结果解析器一跑就 OOM。后来才发现,规则解析不能一股脑全加载。
解析器通常分两步走:
- 词法分析:把规则文本拆成 token,比如
IF、AND、> 1000 - 语法分析:把 token 组装成抽象语法树(AST)
核心要点:解析器输出的是 AST,不是直接可执行的代码。这样做的好处是——你可以对 AST 做优化,比如常量折叠、条件短路,然后再交给执行引擎。
// 规则文本示例
IF user.age > 18 AND user.credit_score >= 600 THEN
APPROVE(limit = 5000)
END
// 解析后的 AST 结构(简化)
{
"type": "Rule",
"condition": {
"type": "AND",
"left": {"type": "GT", "field": "user.age", "value": 18},
"right": {"type": "GTE", "field": "user.credit_score", "value": 600}
},
"action": {
"type": "APPROVE",
"params": {"limit": 5000}
}
}
2. 执行引擎:规则匹配的核心战场
执行引擎是策略引擎的「心脏」。它拿到 AST 和输入数据,然后做条件匹配。我曾经踩过一个坑——用简单的顺序匹配,结果 5000 条规则跑一次要 200 毫秒,完全扛不住线上流量。
后来我换成了 Rete 算法,把规则编译成一个有向无环图(DAG)。
- 顺序匹配:简单粗暴,适合规则少(< 100 条)的场景
- Rete 算法:共享条件节点,适合规则多、条件重复的场景
- 决策树:把规则转成树状结构,匹配效率最高,但维护成本高
我的建议:如果规则数量在 1000 条以内,用 Rete 算法性价比最高。超过 5000 条,考虑决策树或者规则分组。
3. 决策缓存:别让引擎重复造轮子
决策缓存是很多人容易忽略的模块。你想想看,同一个用户在一分钟内请求了 3 次,每次都要跑一遍规则引擎?太浪费了。
缓存策略我一般分三级:
- 请求级缓存:同一个请求内,相同因子只查一次
- 会话级缓存:用户会话期间,部分结果可复用
- 全局级缓存:静态因子(如黑名单)全局共享
注意:缓存一定要设置 TTL,而且要有主动失效机制。我曾经遇到过缓存没及时更新,导致黑名单用户被放行了 10 分钟——那场面,简直灾难。
三、数据流设计:请求在引擎里走了一趟
数据流设计决定了系统的延迟和吞吐。我画了一张图,你看完就明白了。
数据流的核心路径是这样的:
- 请求进来,接入层做参数校验和限流
- 执行引擎先查决策缓存,命中直接返回
- 缓存未命中,引擎从规则解析器拿 AST,从数据层拿因子
- 条件匹配,执行动作,输出结果
- 结果回写缓存,同时写审计日志
关键设计原则:数据流要尽量「无状态」。执行引擎不保存任何中间状态,所有数据要么在请求上下文里,要么在缓存里。这样水平扩展才容易。
四、避坑指南:我踩过的三个坑
做架构设计,光看理论是不够的。我把自己踩过的坑分享给你,希望能帮你少走弯路。
- 坑一:规则解析和执行业务混在一起。我曾经把规则解析放在执行引擎里,结果每次规则变更都要重启服务。后来拆成两个独立模块,规则热加载就轻松了。
- 坑二:缓存穿透。某个高频查询的因子一直不在缓存里,每次都要查数据库。解决方案是加一个「空值缓存」,即使查不到也缓存一个空标记。
- 坑三:数据流死循环。规则 A 的执行结果触发了规则 B,规则 B 又触发了规则 A。我后来加了一个最大递归深度限制,超过 10 层直接报错。
小技巧:在数据流的关键节点埋点,记录每个阶段的耗时。我一般用「请求 ID + 阶段名 + 时间戳」的格式,排查问题的时候特别好用。
好了,架构分层和核心模块就讲到这里。记住一句话:分层清晰,数据流可控,缓存策略合理——这三点做到了,你的策略引擎就成功了一大半。