设计一个轻量级规则引擎,核心是实现数据结构定义、条件解析器和动作执行器。通过将业务逻辑与代码解耦,实现规则的动态配置与高效执行。
为什么需要规则引擎
在日常业务开发中,硬编码(Hardcoding)往往面临以下痛点:
- 营销活动与风控策略频繁变动:每次调整逻辑都需要修改代码、打包、重新上线,回归测试成本高;
- 业务逻辑散落各处:
if-else或switch-case嵌套过深,形成“代码泥潭”,后续维护极易引发 Bug; - 业务人员无法独立配置:规则调整排期依赖开发,导致业务响应滞后。
规则引擎的核心价值:彻底解耦业务规则与系统代码。将“如果满足条件 A,就执行动作 B”的决策逻辑从业务代码中抽离,交由引擎统一管理,实现规则的在线配置、动态更新与实时生效。
核心概念与异同对比
概念区分
- 规则引擎:解决 WHAT 的问题(满足什么条件,执行什么动作),聚焦于业务决策逻辑,通常无固定流程顺序。
- 工作流引擎:解决 HOW 的问题(业务按什么步骤、什么顺序流转),聚焦于流程的审批、分支与退回。
- 流程编排引擎:解决多服务组合调用的问题,聚焦于分布式系统中的服务调用顺序与降级熔断。
硬编码 vs 规则引擎
| 对比维度 | 硬编码实现 | 规则引擎实现 |
|---|---|---|
| 规则与代码耦合度 | 极高,规则写死在代码中 | 极低,规则与代码完全解耦 |
| 规则迭代效率 | 极低(需编译打包上线) | 极高(在线配置,分钟级生效) |
| 规则可维护性 | 极差,散落多处,难排查 | 极好,集中管理、版本控制 |
| 上线风险 | 高(需全量回归) | 低(支持灰度发布与动态热更新) |
规则引擎相关的设计模式
这是关于规则引擎最常被问到的问题之一。直观上,规则引擎允许你在运行时替换不同的"规则策略",看起来很像策略模式(Strategy Pattern),但实际并非如此。
策略模式 vs 规则引擎
| 对比维度 | 策略模式 | 规则引擎 |
|---|---|---|
| 解决的问题 | 算法替换(同接口、不同实现) | 复杂业务规则的编排与执行 |
| 规则数量 | 通常 ≤ 10 个 | 可以成百上千条 |
| 规则来源 | 编码时确定,编译时绑定 | 运行时动态加载(DB、配置文件、DSL) |
| 条件复杂度 | 简单选择逻辑(如 switch) | 支持组合条件、优先级、冲突解决 |
| 核心机制 | 多态 + 接口注入 | 推理引擎(如 Rete 算法)或 AST 求值 |
策略模式的核心:你有一组实现同一接口的类,在编码时就能确定它们的接口和数量,通过简单的选择逻辑(如参数 switch)来替换。这是一个算法替换模式。
规则引擎的核心:规则的数量、内容、编排方式都是运行时才确定的,开发者无法在编译期预见所有规则。引擎需要自己负责条件匹配、优先级排序、冲突解决——这些是策略模式无法覆盖的。
规则引擎是多个设计模式的复合体
规则引擎更像以下模式的组合:
- 解释器模式(Interpreter):规则本身就是一种 DSL(领域特定语言),引擎需要解析规则表达式并构建 AST,然后递归求值。上述 Go 实现中的
RuleNode.Evaluate()就是一个典型的解释器模式应用——每个节点知道如何解释自己。 - 责任链模式(Chain of Responsibility):规则按优先级形成链,引擎逐个评估,直到找到匹配的规则或遍历完所有规则。优先级(Priority)和议程调度器(Agenda)都是责任链思想的体现。
- 组合模式(Composite):
RuleNode既可以包含原子Condition,也可以包含嵌套的子RuleNode,形成树状结构。Evaluate()递归调用自身,正是组合模式的经典特征——单个对象和组合对象拥有一致的接口。
一句话总结
如果你只是把 if-else 提取成多个可替换的策略类,并在编码时选择其中一个,那是策略模式。但一个完整的规则引擎远远超出了策略模式的范畴——它是解释器模式 + 责任链模式 + 组合模式的复合体,再加上自己的推理机制(Rete 算法、AST 求值等)。规则引擎不是策略模式的应用,而是对策略模式的超越和升华。
规则引擎数据结构模型
规则引擎的核心数据结构遵循 Rule → Condition → Action 的三层模型,直观理解如下:
从图中可以清晰地看到:
- Engine 管理 N 条 Rule,每条 Rule 有一个唯一的 ID、名称和优先级。
- 每个 Rule 包含一棵条件树(RuleNode),树的根节点开始,可以递归嵌套 AND/OR 逻辑节点,底层的原子条件(Condition) 由 Field、Operator、Value 三元组构成,对应代码中的
condition.go。 - 条件树的叶子节点是事实(Fact,即业务数据) 的输入入口,引擎将 Fact 传入树根,递归求值。
- 满足条件的 Rule 触发绑定的 Action,Action 执行后返回 Result。
- 子节点通过虚线箭头表示递归同构——每个子节点的结构与根节点完全一致,这是组合模式的体现。
规则引擎架构与核心算法
一个完备的规则引擎架构通常包含以下 5 大核心模块:
- 数据模型 / 事实对象(Fact):引擎输入的业务数据实体。
- 规则管理中心:负责规则的存储、版本控制与热更新通知。
- 规则解析器(Parser):将规则描述文本/JSON 解析为引擎内部的条件树(AST)或 Rete 网络。
- 规则执行引擎(Engine):完成 Fact 与规则条件的模式匹配。
- 议程调度器(Agenda Scheduler):解决规则冲突,按优先级或排序策略依次触发 Action。
从架构图可以清晰地看到上下两条链路:
- 配置与编译链路:规则管理中心 → 解析器 (AST / Rete) → 注入执行引擎
- 匹配与执行链路:Fact 业务数据 → Engine 模式匹配 → Agenda 排序/冲突解决 → Action 输出
底层算法补充: Drools 等企业级引擎多采用 Rete / Phreak 算法,其核心思想是“以空间换时间”,将规则构建为有向无环图,共享公共的条件节点(Alpha/Beta 节点),避免海量规则重复计算条件。
基于 Go 语言设计与实现轻量级规则引擎
下面使用 Go 语言 实现一个基于 抽象语法树 (AST) / 逻辑条件树 的轻量级规则引擎。
核心特性
- 无外部库依赖:纯 Go 原生实现条件匹配(支持
=,!=,>,<,>=,<=,contains)。 - 组合条件表达:支持
AND/OR嵌套组成的逻辑节点树。 - 规则与动作解耦:条件校验与动作执行解耦,执行结果自动追踪。
- 并发安全与热更新:支持在线替换/更新规则,线程安全。
Go 代码实现
package ruleengine
import (
"fmt"
"reflect"
"strings"
"sync"
)
// Operator 比较操作符
type Operator string
const (
OpEq Operator = "="
OpNe Operator = "!="
OpGt Operator = ">"
OpGte Operator = ">="
OpLt Operator = "<"
OpLte Operator = "<="
OpContains Operator = "CONTAINS"
)
// LogicOperator 逻辑操作符
type LogicOperator string
const (
LogicAnd LogicOperator = "AND"
LogicOr LogicOperator = "OR"
)
// Condition 单个条件(原子节点)
type Condition struct {
Field string `json:"field"` // Fact 中的字段 key
Operator Operator `json:"operator"` // 比较操作符
Value interface{} `json:"value"` // 期望目标值
}
// Evaluate 校验单个条件
func (c *Condition) Evaluate(fact map[string]interface{}) bool {
val, exists := fact[c.Field]
if !exists {
return false
}
switch c.Operator {
case OpEq:
return reflect.DeepEqual(val, c.Value)
case OpNe:
return !reflect.DeepEqual(val, c.Value)
case OpGt:
return toFloat64(val) > toFloat64(c.Value)
case OpGte:
return toFloat64(val) >= toFloat64(c.Value)
case OpLt:
return toFloat64(val) < toFloat64(c.Value)
case OpLte:
return toFloat64(val) <= toFloat64(c.Value)
case OpContains:
str, ok := val.(string)
substr, ok2 := c.Value.(string)
return ok && ok2 && strings.Contains(str, substr)
default:
return false
}
}
// RuleNode 表达式节点(可以包含直接条件或子节点逻辑树)
type RuleNode struct {
Logic LogicOperator `json:"logic,omitempty"` // AND / OR
Conditions []Condition `json:"conditions,omitempty"` // 单条件列表
Children []RuleNode `json:"children,omitempty"` // 嵌套子节点
}
// Evaluate 递归求值表达式树
func (n *RuleNode) Evaluate(fact map[string]interface{}) bool {
// 如果是组合逻辑节点 (AND / OR)
if n.Logic == LogicOr {
for _, c := range n.Conditions {
if c.Evaluate(fact) {
return true
}
}
for _, child := range n.Children {
if child.Evaluate(fact) {
return true
}
}
return false
}
// 默认使用 AND 逻辑
for _, c := range n.Conditions {
if !c.Evaluate(fact) {
return false
}
}
for _, child := range n.Children {
if !child.Evaluate(fact) {
return false
}
}
return true
}
// Action 规则命中后的触发动作函数
type Action func(fact map[string]interface{}) interface{}
// Rule 规则对象
type Rule struct {
ID string `json:"id"`
Name string `json:"name"`
Priority int `json:"priority"` // 优先级,越大越优先
RootNode RuleNode `json:"root_node"`
ActionFn Action `json:"-"`
}
// Engine 规则引擎(包含线程安全的热更新与执行)
type Engine struct {
mu sync.RWMutex
rules map[string]*Rule
}
func NewEngine() *Engine {
return &Engine{
rules: make(map[string]*Rule),
}
}
// RegisterRule 注册/更新规则(支持热加载)
func (e *Engine) RegisterRule(rule *Rule) {
e.mu.Lock()
defer e.mu.Unlock()
e.rules[rule.ID] = rule
}
// RemoveRule 卸载规则
func (e *Engine) RemoveRule(ruleID string) {
e.mu.Lock()
defer e.mu.Unlock()
delete(e.rules, ruleID)
}
// Execute 传入 Fact 数据,触发匹配的规则
func (e *Engine) Execute(fact map[string]interface{}) map[string]interface{} {
e.mu.RLock()
defer e.mu.RUnlock()
results := make(map[string]interface{})
for _, rule := range e.rules {
if rule.RootNode.Evaluate(fact) {
if rule.ActionFn != nil {
results[rule.ID] = rule.ActionFn(fact)
} else {
results[rule.ID] = "MATCHED"
}
}
}
return results
}
// 辅助数值转换函数
func toFloat64(val interface{}) float64 {
switch v := val.(type) {
case int:
return float64(v)
case int64:
return float64(v)
case float64:
return v
case float32:
return float64(v)
default:
return 0
}
}
模拟电商风控/折扣场景使用示例
package main
import (
"fmt"
"ruleengine"
)
func main() {
// 1. 初始化规则引擎
engine := ruleengine.NewEngine()
// 2. 配置规则:VIP大额订单打折规则 ( (Amount >= 1000 AND UserLevel == 3) OR UserTags CONTAINS "VIP" )
discountRule := &ruleengine.Rule{
ID: "rule_discount_001",
Name: "大额 VIP 订单打折规则",
Priority: 10,
RootNode: ruleengine.RuleNode{
Logic: ruleengine.LogicOr,
Children: []ruleengine.RuleNode{
{
Logic: ruleengine.LogicAnd,
Conditions: []ruleengine.Condition{
{Field: "amount", Operator: ruleengine.OpGte, Value: 1000},
{Field: "user_level", Operator: ruleengine.OpEq, Value: 3},
},
},
},
Conditions: []ruleengine.Condition{
{Field: "user_tag", Operator: ruleengine.OpContains, Value: "VIP"},
},
},
ActionFn: func(fact map[string]interface{}) interface{} {
amount := fact["amount"].(int)
discountAmount := float64(amount) * 0.8
return fmt.Sprintf("匹配成功!原价 %d,享8折优惠,现价: %.2f", amount, discountAmount)
},
}
// 3. 注册规则到引擎
engine.RegisterRule(discountRule)
// 4. 输入 Fact 事实对象模拟测试
fact1 := map[string]interface{}{
"amount": 1200,
"user_level": 3,
"user_tag": "NORMAL",
}
fact2 := map[string]interface{}{
"amount": 200,
"user_level": 1,
"user_tag": "NORMAL",
}
fmt.Println("--- 测试 Fact 1 (满足大额VIP) ---")
res1 := engine.Execute(fact1)
for ruleID, res := range res1 {
fmt.Printf("[%s]: %v\n", ruleID, res)
}
fmt.Println("\n--- 测试 Fact 2 (不满足规则) ---")
res2 := engine.Execute(fact2)
if len(res2) == 0 {
fmt.Println("未命中任何规则")
}
}
生产落地与治理建议
- 规则日志追踪 (Audit Trail):在引擎执行条件判断时,记录完整评估轨迹,方便问题排查与归因。
- 规则配置持久化与热加载:将规则序列化为 JSON 存入 DB 或配置中心(如 Nacos / Consul),结合 Golang
sync.RWMutex实现配置秒级刷新。 - 语法校验与死循环预防:在规则配置保存阶段提供 AST 表达式的合法性检验,限制最大计算深度。