规则引擎核心概念:规则的定义、优先级、匹配模式与热更新
做市商系统里,规则引擎就像大脑里的「决策中枢」。你想想看,每秒成百上千的订单进来,哪些该接、哪些该拒、哪些要路由到不同交易所——全靠规则引擎来拍板。今天我就把规则引擎的几个核心概念掰开揉碎了讲清楚。
一、规则的定义:不只是「如果-那么」
很多人以为规则就是简单的 if-else。其实在量化系统里,规则是一个结构化的决策单元。我习惯把规则拆成三部分:
- 条件(Condition):触发规则的前提,比如「订单金额 > 100万 USDT」
- 动作(Action):条件满足时执行的操作,比如「拒绝订单并告警」
- 元数据(Metadata):规则的名称、版本、生效时间、创建人等
举个例子,一条典型的风控规则长这样:
{
"ruleId": "RISK_001",
"name": "单笔订单金额上限",
"condition": "order.amount > 1_000_000",
"action": "REJECT + ALERT",
"priority": 10,
"status": "ACTIVE",
"version": 2
}
这里有个细节——条件表达式怎么写。我建议用 DSL(领域特定语言)而不是硬编码。我在项目中遇到过,早期用 Java 代码写规则,每次改规则都要重新编译部署,太痛苦了。后来换成 Groovy 脚本或者 MVEL 表达式,改规则就像改配置文件一样简单。
三、规则的优先级:谁先说话?
当多条规则同时匹配时,谁先执行?这就涉及到优先级了。我见过不少系统因为优先级没设计好,导致「白名单规则被黑名单规则覆盖」的惨案。
优先级通常用数字表示,数字越小优先级越高。我个人习惯这样划分:
| 优先级范围 | 用途 | 示例 |
|---|---|---|
| 1-10 | 系统级规则(不可绕过) | 账户冻结、交易所熔断 |
| 11-50 | 风控规则 | 金额上限、频率限制 |
| 51-100 | 业务规则 | 路由选择、费率优化 |
| 101+ | 默认规则 | 兜底策略 |
为什么要这样分层?因为系统级规则必须优先执行。我曾经遇到过一个 bug:某条业务规则的优先级设成了 5,结果它先于「账户冻结」规则执行了。用户账户明明被冻结了,业务规则还傻乎乎地去路由订单——嗯,那笔订单直接飞出去了,还好被交易所风控拦住了。
三、规则的匹配模式:白名单 vs 黑名单
匹配模式说白了就是「允许谁」和「拒绝谁」。这两个模式看似简单,但用错了会出大问题。
白名单模式
白名单的意思是:只有名单里的才允许通过。比如「只有 VIP 账户才能使用高频交易策略」。白名单的优点是安全可控,缺点是维护成本高——每加一个新用户都要改规则。
黑名单模式
黑名单的意思是:名单里的拒绝,其他都放行。比如「禁止 IP 段 192.168.1.x 访问交易接口」。黑名单的优点是灵活,但风险也大——万一漏了一条黑名单规则,坏人就进来了。
我个人的经验是:风控场景用白名单,业务场景用黑名单。举个例子:
- 风控规则:只有白名单里的交易所才能交易(防止误连钓鱼交易所)
- 业务规则:黑名单里的币种不能做市(其他币种都可以)
你可能会问:「能不能混用?」当然可以。我做过一个系统,规则引擎支持同时配置白名单和黑名单,逻辑是:先检查黑名单,再检查白名单。如果既不在黑名单也不在白名单,就走默认策略。
四、规则的动态加载与热更新
做市商系统是 7x24 小时运行的,你不能说「我要改一条规则,先停个机」。所以热更新是必备能力。
热更新的核心就三个字:不停机。具体怎么做?我分享一个我常用的方案:
- 规则存储:规则存在数据库或配置中心(比如 ZooKeeper、etcd)
- 规则缓存:引擎启动时加载到本地内存,用 ConcurrentHashMap 或 Guava Cache 存着
- 监听变更:监听配置中心的变更事件,或者定时轮询数据库(比如每 5 秒检查一次版本号)
- 原子切换:用读写锁或 CopyOnWrite 机制,保证规则切换时不会出现「半条规则」的情况
代码示例(伪代码):
public class RuleEngine {
private volatile Map<String, Rule> rules = new ConcurrentHashMap<>();
public void refreshRules() {
// 从数据库加载最新规则
List<Rule> newRules = loadRulesFromDB();
// 原子替换
this.rules = newRules.stream()
.collect(Collectors.toConcurrentMap(Rule::getId, r -> r));
}
public RuleResult evaluate(Order order) {
// 遍历规则,按优先级执行
for (Rule rule : rules.values().stream()
.sorted(Comparator.comparingInt(Rule::getPriority))
.collect(Collectors.toList())) {
if (rule.matches(order)) {
return rule.execute(order);
}
}
return RuleResult.PASS;
}
}
这里有个坑:规则热更新时,正在执行的订单怎么办?我建议用「版本号」机制。每条规则带一个版本号,订单进来时记录当前规则版本。如果规则更新了,正在执行的订单继续用旧版本,新订单用新版本。这样就不会出现「同一个订单被新旧规则各判一次」的混乱。
五、知识体系总览
说了这么多,我画了一张图帮你梳理一下规则引擎的核心逻辑:
这张图把四个核心概念串起来了。你看,规则定义是基础,优先级决定执行顺序,匹配模式决定「谁进谁出」,热更新保证系统持续运行。四者缺一不可。
好了,规则引擎的核心概念就讲到这里。记住一句话:规则是死的,但引擎是活的。设计时多想想「如果规则变了怎么办」,你的系统会健壮很多。
公众号:蓝海资料掘金营,微信deep3321