3. 规则引擎选型:轻量级规则引擎对比

说实话,选规则引擎这事儿,我纠结了挺久。

当时团队里有人提议直接用 Drools,说它功能强大、社区成熟。也有人觉得 Easy Rules 够轻量,上手快。但我最后选了自研 JSON 规则。为什么?

咱们先看看这三条路各自长什么样。

3.1 三个候选方案速览

维度 Drools Easy Rules 自研 JSON 规则
学习成本 高(DRL 语法、KIE 体系) 中(注解 + 规则类) 低(JSON 配置即可)
部署体积 ~15MB(依赖庞大) ~200KB 零额外依赖
动态更新 支持(但需重新编译) 需重启或自定义加载 天然支持(热加载 JSON)
API 层适配 重,需单独部署或嵌入 中等,可嵌入 极轻,直接嵌入
调试难度 高(规则堆栈复杂) 中 低(JSON 可读性强)

核心结论:在 API 层做数据质量校验,我们需要的不是「万能规则引擎」,而是「刚好够用、随时可改、不拖累性能」的轻量方案。

3.2 Drools:杀鸡用牛刀

Drools 确实强。Rete 算法、前向链、后向链、复杂事件处理…… 这些能力在风控、审批流场景里很香。但咱们这是 API 数据质量校验啊。

我举个例子。你只是想检查「手机号不能为空」「金额必须大于 0」,Drools 却要你写 DRL 文件、配置 kmodule.xml、启动 KieContainer。这感觉就像用火箭筒打蚊子。

我在项目中见过一个团队,为了用 Drools 专门配了个规则管理后台,结果上线后每次改规则都要走发布流程。运维同学气得直拍桌子。

避坑:我曾经接手过一个项目,Drools 规则库膨胀到 200+ 条后,单次规则评估耗时从 2ms 飙升到 50ms。排查了三天,发现是规则间冲突导致 Rete 网络退化。对于 API 层来说,这种不确定性不可接受。

3.3 Easy Rules:轻了,但不够灵活

Easy Rules 是个好框架。注解驱动,几行代码就能跑起来。我刚开始也倾向它。

但用着用着就发现问题了:

  • 规则定义耦合 Java 代码——每次加规则都要改代码、重新打包。
  • 组合逻辑表达能力弱——AND/OR/NOT 嵌套多了,代码可读性直线下降。
  • 无法动态下发热加载——虽然可以自己写个 JSON 解析器转成 Easy Rules 的 Rule 对象,但那不就是自研了吗?

说白了,Easy Rules 适合「规则固定、变化少」的场景。但数据质量规则恰恰是变化最频繁的——业务方今天说要加个身份证校验,明天说手机号格式要改。你总不能每次都发版吧?

3.4 自研 JSON 规则:我的选择

好,重点来了。为什么我最终选了自研?

其实就三个字:刚刚好。

3.4.1 规则长什么样

先给你看个例子。这是一条「用户注册」接口的数据质量规则:

{
  "ruleId": "RULE001",
  "name": "用户注册信息校验",
  "description": "校验手机号、邮箱、年龄",
  "priority": 1,
  "conditions": {
    "operator": "AND",
    "rules": [
      {
        "field": "phone",
        "operator": "NOT_EMPTY",
        "message": "手机号不能为空"
      },
      {
        "field": "phone",
        "operator": "REGEX",
        "value": "^1[3-9]\\d{9}$",
        "message": "手机号格式不正确"
      },
      {
        "field": "email",
        "operator": "REGEX",
        "value": "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$",
        "message": "邮箱格式不正确"
      },
      {
        "operator": "OR",
        "rules": [
          {
            "field": "age",
            "operator": "BETWEEN",
            "value": [18, 60]
          },
          {
            "field": "role",
            "operator": "IN",
            "value": ["admin", "super_admin"]
          }
        ],
        "message": "普通用户年龄需在18-60岁之间,管理员不受限制"
      }
    ]
  },
  "actions": [
    {
      "type": "REJECT",
      "httpStatus": 400
    }
  ]
}

设计思路:JSON 规则的核心是「条件树」。每个节点可以是叶子节点(字段 + 操作符 + 值),也可以是组合节点(AND/OR/NOT)。这样递归解析,天然支持任意复杂度的嵌套逻辑。

3.4.2 引擎核心代码(简化版)

public class JsonRuleEngine {
    
    public RuleResult evaluate(JsonNode ruleNode, Map<String, Object> data) {
        String operator = ruleNode.get("operator").asText();
        
        switch (operator) {
            case "AND":
                return evaluateAnd(ruleNode.get("rules"), data);
            case "OR":
                return evaluateOr(ruleNode.get("rules"), data);
            case "NOT":
                return evaluateNot(ruleNode.get("rules").get(0), data);
            default:
                return evaluateLeaf(ruleNode, data);
        }
    }
    
    private RuleResult evaluateLeaf(JsonNode leaf, Map<String, Object> data) {
        String field = leaf.get("field").asText();
        String op = leaf.get("operator").asText();
        Object value = data.get(field);
        
        boolean passed = switch (op) {
            case "NOT_EMPTY" -> value != null && !value.toString().isEmpty();
            case "REGEX" -> value != null && value.toString().matches(leaf.get("value").asText());
            case "BETWEEN" -> {
                int v = Integer.parseInt(value.toString());
                ArrayNode range = (ArrayNode) leaf.get("value");
                yield v >= range.get(0).asInt() && v <= range.get(1).asInt();
            }
            case "IN" -> {
                ArrayNode allowed = (ArrayNode) leaf.get("value");
                yield StreamSupport.stream(allowed.spliterator(), false)
                    .anyMatch(item -> item.asText().equals(value.toString()));
            }
            default -> false;
        };
        
        return new RuleResult(passed, passed ? null : leaf.get("message").asText());
    }
}

你看,核心逻辑不到 50 行。递归遍历条件树,每个节点只做一件事。性能稳定,O(n) 复杂度,n 是规则节点数。

3.4.3 为什么我选它

  • 零依赖——一个 JSON 解析库就够了,部署包不增肥。
  • 热加载——规则存数据库或配置中心,改完秒级生效。
  • 可读性强——业务方都能看懂 JSON,自己就能配规则。
  • 性能可控——没有黑盒优化,每行代码都看得见。
  • 扩展容易——加个操作符?加个枚举就行。

我的经验:自研规则引擎上线后,规则变更周期从「3 天(提测 + 发布)」缩短到「5 分钟(改配置 + 刷新)」。运维同学终于不用半夜爬起来发版了。

3.5 规则引擎选型决策树

为了让你更直观地理解选型逻辑,我画了张图:

规则引擎选型决策树 规则引擎选型 规则复杂度高? 规则变化频繁? 是 是 Drools 自研 JSON 规则 否 规则数量少? 是 否 Easy Rules 自研 JSON 规则 选型建议: • 规则复杂、变化少、团队有 Drools 经验 → Drools • 规则简单、数量少、变化不频繁 → Easy Rules • 规则变化频繁、需要热加载、API 层嵌入 → 自研 JSON 规则

3.6 自研的代价与边界

当然,自研不是万能的。我得跟你说清楚它的代价:

  • 没有规则冲突检测——Drools 的 Rete 网络会自动检测冲突,自研得自己写。
  • 没有规则优先级调度——复杂场景下需要自己实现 Agenda 排序。
  • 没有规则可视化编辑器——得自己搭或者让业务方直接写 JSON。

我的建议:如果你的规则超过 500 条,或者需要复杂的推理链(比如 A 规则触发 B 规则,B 规则再触发 C 规则),那还是老老实实用 Drools。自研 JSON 规则最适合「校验型」场景——每条规则独立,不依赖其他规则的执行结果。

说白了,选型没有银弹。你得想清楚自己的场景:

  • 是「校验」还是「推理」?
  • 是「静态」还是「动态」?
  • 是「嵌入」还是「独立部署」?

对于 API 层数据质量校验这个场景,我的答案很明确:自研 JSON 规则。轻、快、可控。够用就好。