⭐⭐⭐ Spring Boot 项目实战 ⭐⭐⭐ Spring Cloud 项目实战
《Dubbo 实现原理与源码解析 —— 精品合集》 《Netty 实现原理与源码解析 —— 精品合集》
《Spring 实现原理与源码解析 —— 精品合集》 《MyBatis 实现原理与源码解析 —— 精品合集》
《Spring MVC 实现原理与源码解析 —— 精品合集》 《数据库实体设计合集》
《Spring Boot 实现原理与源码解析 —— 精品合集》 《Java 面试题 + Java 学习指南》

摘要: 原创出处 http://www.iocoder.cn/Fight/tuijianyikuanjiyuWebkeshihuapeizhikelingdaimashixianfuzayewuluoji/ 「芋道源码」欢迎转载,保留摘要,谢谢!


🙂🙂🙂关注微信公众号:【芋道源码】有福利:

  1. RocketMQ / MyCAT / Sharding-JDBC 所有源码分析文章列表
  2. RocketMQ / MyCAT / Sharding-JDBC 中文注释源码 GitHub 地址
  3. 您对于源码的疑问每条留言都将得到认真回复。甚至不知道如何读源码也可以请教噢。
  4. 新的源码解析文章实时收到通知。每周更新一篇左右。
  5. 认真的源码交流微信群。

反向定位:把规则改动从代码里剥出来

风控、营销、定价、优惠这些业务规则的共性是:变得快、变得乱、变得不讲道理。运营今天说「白名单加 3 个手机号」、明天说「下单金额 > 500 才发优惠券」、后天又说「IP 黑名单临时屏蔽 50 个段」。

如果这些规则全写在代码里:

  • 改一条 → 改 Java 代码 → CR → CI → 灰度 → 全量 → 半天到一天没了;
  • 紧急变更等不及发版,运营拿 SQL 直连库改 → 没审计、没回滚;
  • 月底复盘时谁也说不清楚「这条规则当时为什么开」。

这次推荐的是 RuleEngine——一个开源 Java 规则引擎,它不是「让你写更优雅的 if-else」,是「把『改规则』这件事从代码里剥出来交给运营/PM 自助」:

  • Web 界面配规则:运营自己拖拽组合「条件 + 阈值 + 动作」,不需要工程师;
  • HTTP 接口执行:Java 服务调一下 /ruleEngine/generalRule/execute,传业务参数,回来就是判定结果;
  • 零发版生效:Web 上保存即生效,无需重启业务服务。

核心理念:业务规则的变化频率,远高于代码本身的变化频率——把它们解耦开,是工程纪律。

横向对比:4 个规则引擎方案怎么选

不是所有项目都该上规则引擎。先看自己在哪个象限:

方案 形态 配置难度 改完是否要发版 致命短板
硬编码 if-else 代码 0 每次都要发版 改一条要走完整 CI/CD
Drools Java 库 高 改 DRL 文件、要部署 DSL 学习曲线陡,运营写不来
Groovy / QLExpress 脚本 嵌入式 中 字符串热更新可省发版 仍是写代码,运营碰不了
本文方案:Web 规则引擎 独立服务 低(拖拽) 零发版 多一个服务依赖

判断很简单:

  • 规则数量 < 5 条、几乎不变:硬编码就好,别上重武器;
  • 规则在工程师内部维护:Groovy / QLExpress 脚本 是最轻的活路,能热更新;
  • 规则要交给运营 / PM 自己改、变更频繁:Web 规则引擎是唯一答案;
  • 真复杂的工业级规则(保险定价、信贷决策):Drools 仍有它的位置。

3 个真实业务场景

场景 1:风控黑白名单

规则:当 phone IN 黑名单库 OR ip IN 屏蔽段 → 拒绝下单

痛点:黑名单每天都在变,每次都改代码不现实。
用法:运营在 Web 配「条件 + 黑名单数据源」,下单接口执行规则前调一次:

PhoneBlockRule input = new PhoneBlockRule();
input.setPhone(req.getPhone());
input.setIp(req.getClientIp());
Output result = ruleEngineClient.generalRule().execute(input);
if (result.isHit()) throw new BlockedException("命中风控");

收益:黑名单变更秒级生效,运营自助操作,开发者解放。

场景 2:优惠券发放规则

规则:用户 是新客 AND 下单金额 > 500 AND 城市 IN [北京/上海/深圳] → 发 50 元券

痛点:大促规则一天一变,全靠开发改 if-else 极其折磨。
用法:每个促销活动配成一条规则,结束直接禁用——库存、券发放走原系统接口,规则引擎只负责「判定是否发」。
收益:促销上线时间从 3 天压到 1 小时。

场景 3:参数动态校验

规则:手机号长度 = 11 AND 前缀 IN [13/14/15/16/17/18/19] AND 不在内部测试号段

痛点:测试号段、临时灰度号段经常变,校验代码改起来繁琐。
收益:校验逻辑放规则引擎,业务代码就保留「调一次规则引擎」一行。

接入 3 步:装服务 + 装客户端 + 调规则

第 1 步:装规则引擎服务

SpringBoot 2.2.11 + RabbitMQ + Redis + MySQL 5.7+ + MyBatis-Plus

部署后访问 Web 控制台 http://ruleengine.cn,配规则。

第 2 步:业务系统接客户端

<dependency>
<groupId>cn.ruleengine</groupId>
<artifactId>rule-engine-client</artifactId>
<version>2.0</version>
</dependency>

application.yml:

rule:
engine:
baseUrl: http://ruleserver.cn
workspaceCode: default
accessKeyId: root
accessKeySecret: 123456
feignConfig: # 可选
request:
connectTimeoutMillis: 3000
readTimeoutMillis: 3500
retryer:
period: 2000
maxPeriod: 2000
maxAttempts: 3

关键点:超时时间 + 重试次数要根据业务调——规则引擎挂了不能让业务接口跟着雪崩。

第 3 步:业务代码调规则

@EnableRuleEngine
@SpringBootTest
public class RuleTest {

@Resource
private RuleEngineClient ruleEngineClient;

@Test
public void testPhoneRule() {
PhoneTestRule input = new PhoneTestRule();
input.setPhone("13400000000");

GeneralRule rule = ruleEngineClient.generalRule();
Output output = rule.execute(input);
System.out.println(output);
}
}

@Data
@Model(code = "phoneRuletest")
public class PhoneTestRule {
@InputParameter(code = "phone")
private String phone;
}

注意:@Model(code = "phoneRuletest") 必须和 Web 上配置的规则 code 完全对齐,错一个字母规则就调不到。

也可以走 HTTP 不用客户端(适合非 Java 项目):

POST http://ruleserver.cn/ruleEngine/generalRule/execute
Content-Type: application/json

{
"code": "phoneRuletest",
"workspaceCode": "default",
"accessKeyId": "略",
"accessKeySecret": "略",
"param": { "phone": "13400000000" }
}

自定义 HTTP 客户端(用 RestTemplate 替代默认 Feign):

@Component
@Import({RestTemplate.class})
public class RuleEngineClientConfig {

@Resource private RestTemplate restTemplate;

@Bean
public GeneralRuleInterface generalRuleInterface() {
return new GeneralRuleInterface() {
@Override
public ExecuteResult execute(ExecuteParam param) {
return restTemplate.postForObject(
"http://ruleserver.cn/ruleEngine/generalRule/execute",
param, ExecuteResult.class);
}
@Override public IsExistsResult isExists(IsExistsParam p) { return null; }
@Override public BatchExecuteResult batchExecute(BatchParam p) { return null; }
};
}
}

可视化配置面板

实际操作界面长这样——条件、阈值、动作都拖拽配置:

最直接的好处:运营 / PM 不需要懂 Java 也能配。把这种 UI 截图发给业务方,他们看一眼就懂——比让他们读 if-else 代码门槛低 100 倍。

4 个真实使用边界

边界 1:规则引擎挂了不能让业务接口跟着挂(最常见)

业务接口里调规则引擎的接口,必须设超时 + 兜底策略:超时不返回时走默认放行 / 默认拒绝(按业务决定)。没兜底就是把所有业务接口的 SLA 绑给了规则引擎——这是规则引擎的头号坑。

边界 2:高频规则要加缓存(常见)

每次下单都调,QPS 一上去引擎被打满。修法:客户端对「规则版本号 + 入参」做短期缓存(30 秒~5 分钟),版本变更失效;或把规则推到 Redis 让业务侧本地匹配,引擎只管编译。

边界 3:规则变更要审计 + 灰度(少见但破坏力大)

「运营改规则秒生效」是优点也是风险——运营手抖把白名单全清了,业务直接挂。必须配:① 规则变更审计日志;② 重大规则变更要双人审批;③ 改完先灰度 10% 流量观察 5 分钟再全量。生产规则引擎和数据库是同一个量级的关键基础设施。

边界 4:复杂规则别硬塞(高级场景)

规则引擎适合「条件组合判定 + 阈值比较」这类清晰逻辑——不要硬塞循环、递归、复杂数学计算。复杂的算 score(用户行为) > threshold 应该走专门的算法服务,规则引擎只调用算法服务的输出做最终判定。

最后聊两句

规则引擎不是「把代码搬到 Web 上」——它是把「规则的变更频率」和「代码的变更频率」解耦。好的工程结构不是没有变化,是让易变的部分不影响稳定的部分。

仓库:gitee.com/aizuda/rule-engine-front-open

文章目录
  1. 1. 反向定位:把规则改动从代码里剥出来
  2. 2. 横向对比:4 个规则引擎方案怎么选
  3. 3. 3 个真实业务场景
  4. 4. 接入 3 步:装服务 + 装客户端 + 调规则
  5. 5. 可视化配置面板
  6. 6. 4 个真实使用边界
  7. 7. 最后聊两句