# 规则引擎

规则引擎实现了将业务决策从应用程序代码中分离出来,接受数据输入,解释业务规则,并根据业务规则做出业务决策。规则引擎其实就是一个输入输出平台。

它将复杂多变、经常需要调整的业务规则(例如:如果用户是VIP且订单金额超过500元,那么自动免运费)从固定的程序代码中剥离出来,单独进行管理。这样一来,当业务策略需要变更时(比如把免运费门槛提高到800元),业务人员无需找程序员修改代码和重新部署系统,只需在规则引擎中简单调整规则即可立即生效。

# 常见的规则引擎

通过界面配置的成熟规则引擎
# 这种规则引擎相对来说就比较重,但是功能全,也有部分业务会选择这个,比较出名的有:Drools、URule
# Drools 中文网 : http://www.drools.org.cn
# URule 官网 : http://www.bstek.com

基于JVM脚本语言
# 这种其实不是一个成熟的规则引擎,应该算是规则引擎中的核心技术,因为Drools这种相对太重了
# 很多公司会基于一些jvm的脚本语言,开发轻量级的规则引擎,如:Groovy、AviatorScript、QLExpress
# Groovy 官网 : http://www.groovy-lang.org
# AviatorScript 文档 : https://www.yuque.com/boyan-avfmj/aviatorscript
# QLExpress GitHub : https://github.com/alibaba/QLExpress

基于java代码的规则引擎
# 上面是基于jvm脚本语言去做的,会有一些语法学习的成本,所以就有基于java代码去做的规则引擎
# 比如通过一些注解实现抽象的方式去做到规则的扩展,比较出名的有: Easy Rules
# Easy Rules GitHub : https://github.com/j-easy/easy-rules
# jvs-rules : 可视化的业务规则设计器 https://gitee.com/software-minister/jvs-rules

# AviatorScript

# AviatorScript 编译和执行的入口是 AviatorEvaluatorInstance 类
# 该类的一个实例就是一个编译和执行的单元,这个单元我们称为一个 AviatorScript 引擎
# 你可以多个引擎,每个引擎可以设置不同的编译和运行选项
# AviatorEvaluator.getInstance() 返回一个全局共享的 AviatorScript 引擎

# 编译脚本文件

  • 在你的 java 项目里引用下 AviatorScript 的依赖
<dependency>
  <groupId>com.googlecode.aviator</groupId>
  <artifactId>aviator</artifactId>
  <version>5.3.1</version>
</dependency>
  • 编写你的第一个 AviatorScript 脚本,AviatorScript 脚本源码文件约定以 .av 作为文件后缀
## av/hello.av  两个 # 号引入一行注释

println("hello, AviatorScript!");
  • 编写一个类来运行测试脚本
package com.example.springbootdemo.test;

import com.googlecode.aviator.AviatorEvaluator;
import com.googlecode.aviator.AviatorEvaluatorInstance;
import com.googlecode.aviator.Expression;

public class AviatorScriptDemo {

    public static void main(String[] args) throws Exception {

        // 使用 AviatorEvaluatorInstance#compileScript 方法编译脚本到 Expression 对象
        Expression exp = AviatorEvaluator.getInstance().compileScript("av/hello.av");
        // 调用 Expression#execute() 方法执行
        exp.execute();
    }
}

# Drools

Drools 核心组件:

# KIE(Knowledge Is Everything):统一知识管理容器
# DRL(Drools Rule Language):规则描述语言
# Guvnor(Workbench):可视化规则设计器
# Phreak 算法:新一代高效规则匹配引擎

# Spring Boot 集成实战

  • 依赖配置
<dependency>
    <groupId>org.drools</groupId>
    <artifactId>drools-spring-boot-starter</artifactId>
    <version>7.73.0.Final</version>
</dependency>
<dependency>
    <groupId>org.kie</groupId>
    <artifactId>kie-spring</artifactId>
    <version>7.73.0.Final</version>
</dependency>
  • 规则引擎配置类
@Configuration
public class DroolsConfig {

    private static final String RULES_PATH = "rules/";

    // 创建 Drools 的文件系统 Bean,用于管理规则文件
    @Bean
    public KieFileSystem kieFileSystem() throws IOException {
		// 获取 KieServices 实例并创建新的内存文件系统
        KieFileSystem kieFileSystem = getKieServices().newKieFileSystem();
        Files.list(Paths.get(RULES_PATH)).forEach(path -> {
			// 将每个规则文件写入到 KieFileSystem 中
            kieFileSystem.write(ResourceFactory.newFileResource(path.toFile()));
        });
        return kieFileSystem;
    }

    // Drools 的核心容器,包含所有编译后的规则
    @Bean
    public KieContainer kieContainer() throws IOException {
        KieServices kieServices = getKieServices();
        KieRepository kieRepository = kieServices.getRepository(); // 规则仓库
        // 使用文件系统中的规则创建构建器
        KieBuilder kieBuilder = kieServices.newKieBuilder(kieFileSystem()).buildAll();
        // 获取编译过程中的所有消息(包括错误、警告)
        kieBuilder.getResults().getMessages().forEach(m -> 
            System.out.println("规则编译: " + m)
        );
        // 创建并返回 KieContainer,使用默认的发布 ID
        return kieServices.newKieContainer(kieRepository.getDefaultReleaseId());
    }

    // 实际执行规则的会话
    @Bean
    public KieSession kieSession() throws IOException {
		// 从 KieContainer 创建新的规则会话并返回
        return kieContainer().newKieSession();
    }
}

// 这段配置代码完成了 Drools 规则引擎的完整初始化流程:
// 1、扫描 rules/ 目录下的规则文件
// 2、将规则文件加载到内存文件系统
// 3、编译规则
// 4、创建规则容器
// 5、提供规则会话供业务代码使用

# 可视化规则设计(Workbench)

  • 部署 Drools Workbench
# 访问 http://localhost:8080/business-central (admin/admin)
docker run -p 8080:8080 -p 8001:8001 -d --name drools-workbench jboss/drools-workbench-showcase:7.73.0.Final
  • 创建决策表
条件 动作
会员等级=VIP 折扣率=0.8
购物车金额>1000 折扣率=0.85
活动期间 && 新用户 折扣率=0.7
  • 规则版本管理
// 获取特定版本规则
KieContainer container = kieServices.newKieContainer(
    kieServices.newReleaseId("com.example", "discount-rules", "1.2.0")
);

# 核心源码解析

  • 规则匹配引擎(Phreak算法)
public class PhreakRuleEngine implements RuleEngine {
    public void fireRules(WorkingMemory wm) {
        // 1. 创建规则网络
        RuleNetwork network = buildRuleNetwork(rules);

        // 2. 事实对象传播
        for (Object fact : wm.getFacts()) {
            network.insert(fact);
        }

        // 3. 执行匹配(惰性求值)
        List<Activation> activations = network.match();

        // 4. 按优先级执行
        activations.sort(Comparator.reverseOrder());
        for (Activation act : activations) {
            act.execute();
        }
    }
}
  • Spring集成适配器
public class KieAutoConfiguration {

    @Bean
    @ConditionalOnMissingBean
    public KieContainer kieContainer() {
        return KieServices.Factory.get().getKieClasspathContainer();
    }

    @Bean
    @ConditionalOnMissingBean
    public KieSession kieSession(KieContainer kieContainer) {
        return kieContainer.newKieSession();
    }

    @Bean
    public DroolsRuleEngine droolsRuleEngine(KieSession kieSession) {
        return new DefaultDroolsRuleEngine(kieSession);
    }
}
  • 规则热更新机制
@RestController
public class RuleUpdateController {

    @Autowired
    private KieContainer kieContainer;

    @PostMapping("/reload-rules")
    public void reloadRules() {
        // 触发规则重新加载
        kieContainer.updateToVersion(
            kieContainer.getReleaseId().getVersion()
        );
    }
}

# 企业级最佳实践

  • 规则版本灰度发布
public class RuleVersionRouter {

    @Value("${rule.version.weights}")
    private Map<String, Double> versionWeights;

    public KieSession getVersionedSession() {
        String version = selectVersionByWeight();
        return kieContainer.newKieSession(version);
    }

    private String selectVersionByWeight() {
        double rand = Math.random();
        double cumulative = 0.0;

        for (Map.Entry<String, Double> entry : versionWeights.entrySet()) {
            cumulative += entry.getValue();
            if (rand <= cumulative) {
                return entry.getKey();
            }
        }
        return "default";
    }
}
  • 规则执行监控
public class RuleExecutionListener extends DefaultAgendaEventListener {

    @Override
    public void afterMatchFired(AfterMatchFiredEvent event) {
        Rule rule = event.getMatch().getRule();
        long duration = System.currentTimeMillis() - startTime;

        // 记录规则执行日志
        RuleLog log = new RuleLog(
            rule.getName(), 
            duration, 
            event.getMatch().getObjects()
        );

        // 发送到监控系统
        metricsService.recordRuleExecution(log);
    }
}

// 注册监听器
kieSession.addEventListener(new RuleExecutionListener());
  • 性能优化策略
// 启用无状态会话(无会话缓存)
StatelessKieSession session = kieContainer.newStatelessKieSession();

// 批量执行优化
List<Object> facts = Arrays.asList(order1, order2, order3);
session.execute(facts); // 一次引擎调用处理所有事实

// 规则分区执行
@Bean
public KieBase kieBase() {
    KieBaseConfiguration config = KieServices.Factory.get().newKieBaseConfiguration();
    config.setOption(PartitionEvaluationOption.PARTITIONED); // 启用分区
    return kieContainer.newKieBase(config);
}

# 实战:电商促销系统开发

  • 领域模型设计
public class PromotionContext {
    private User user;
    private Order order;
    private List<Product> products;
    private double finalDiscount = 1.0; // 默认无折扣
    private List<String> appliedRules = new ArrayList<>();
}

public class User {
    private String level; // VIP, NORMAL
    private int points;
    private boolean isNew;
}
  • 规则服务层
@Service
public class PromotionService {

    @Autowired
    private KieSession kieSession;

    public PromotionContext applyPromotions(PromotionContext context) {
        // 插入事实对象
        kieSession.insert(context.getUser());
        kieSession.insert(context.getOrder());
        context.getProducts().forEach(kieSession::insert);

        // 全局变量设置
        kieSession.setGlobal("logger", LoggerFactory.getLogger(getClass()));

        // 执行规则
        kieSession.insert(context);
        kieSession.fireAllRules();

        return context;
    }
}
  • 可视化规则设计(DRL示例)
rule "NewUserWelcomeDiscount"
    when
        $user : User(isNew == true)
        $ctx : PromotionContext(user == $user)
    then
        $ctx.setFinalDiscount(0.8);
        $ctx.addAppliedRule("新用户首单8折");
end

rule "VIPBirthdayDiscount"
    when
        $user : User(level == "VIP", birthday == today())
        $ctx : PromotionContext(user == $user)
    then
        $ctx.setFinalDiscount(0.7);
        $ctx.addAppliedRule("VIP生日特惠7折");
end

# 生产环境避坑指南

  • 规则冲突解决
// 显式设置规则优先级
rule "HighPriorityRule"
    salience 100 // 优先级数值越高越先执行
    when ... then ...
end
  • 循环规则防护
// 设置最大规则触发次数
KieSessionConfiguration config = KieServices.Factory.get()
    .newKieSessionConfiguration();
config.setProperty("drools.maxRuleExecutions", "10000");
  • 内存泄漏预防
try (KieSession session = kieContainer.newKieSession()) {
    // 使用try-with-resources自动释放资源
    session.insert(fact);
    session.fireAllRules();
}