世界万事万物皆有规则java
提及规则引擎, 相信不少小伙伴对于规则引擎产生了不少疑问. 它是什么? 它能作啥? 应该怎么作? 但愿经过阅读下面的内容能给你一些启发.git
首先规则引擎是什么,咱们来看下百度百科是怎么定义的github
规则引擎由推理引擎发展而来,是一种嵌入在应用程序中的组件,实现了将业务决策从应用程序代码中分离出来,并使用预约义的语义模块编写业务决策。接受数据输入,解释业务规则,并根据业务规则作出业务决策。golang
上面说的很清晰, 总结一句话规则引擎作的事情就是 录入特定判断逻辑, 经过输入的数据进行决策api
首先咱们有个基本的优缺点分析:markdown
规则引擎带来的优势:架构
高灵活性并发
高灵活性带来的直接效果是缩短开发到上线周期, 热更新修复bugide
天生组件化函数
简化复杂的业务逻辑, 缩减业务代码量, 易于业务逻辑管理.
规则引擎带来的缺点:
引入了额外服务依赖
对于一些对稳定性、正确性要求极高的场景, 前期不建议引入 (须要提供完善的权限控制和规则单元测试能力)
前期增长产品、技术学习成本
产品须要具备必定抽象思惟, 需求文档中给出系统易变部分进行抽象处理
研发须要学习部分规则语法, 并了解系统实现和约束
并不能依赖规则热更新知足全部业务断定场景
规则引擎/指标 | drools | gengine |
---|---|---|
上手难度 | 有必定门槛 | 易 |
运行方式 | 仅支持顺序型 | 支持顺序/并行/N-M等模式 |
开发语言 | java | golang |
社区活跃度 | 高 | 通常 |
基于本人水平, 此处选择了更易学习的 gengine 做为研究对象 (虽然规则引擎有不一样的运行模式和语言, 但对于咱们理解本质并没什么区别)
支持部分规则模式
运行模式 | 方法名 | 含义 |
---|---|---|
顺序型 | ExecuteSelectedRulesWithControlAndStopTag | 按规则优先级执行(从上往下)-耗时为全部规则执行时间累加 |
并发型 | ExecuteSelectedRulesConcurrent | 全部规则并发执行(执行时间为执行时间最长的-考虑池限制) |
混合模式 | ExecuteSelectedRulesMixModel | 先执行一个优先级最高的规则,而后并发执行剩下的全部规则 |
N-M | ExecuteNConcurrentMConcurrent | 前N个规则并发执行, M个规则也并发执行/ 前N个规则顺序执行, M个规则并发执行 |
制定规则中依赖的方法(硬编码)
type ruleResponse struct {
Code int
Data map[string]interface{}
}
func success(code int) ruleResponse {
return ruleResponse{
Code: code,
Data: make(map[string]interface{}),
}
}
func (rule ruleResponse) AddDataParam(name string, value interface{}) {
rule.Data[name] = value
}
复制代码
规则中是不支持自定义函数和结构体的, 当须要返回非int、字符串、bool值的时候, 须要咱们外部注入对应实现方法, 供规则内调用 (例如自定义结构体、自定义复杂规则验证、获取订单课程等方法)
声明规则
rule1 := ` rule "case0" "case1测试用例" salience 0 //后执行 begin Print(flag-2) successObj := success(0) successObj.AddDataParam("name0","test 000 value") return successObj end rule "case1" "case1测试用例" salience 1 //后执行 begin a = 8 if a < 1 { println("a < 1") } else if a >= 1 && a <6 { println("a >= 1 && a <6") } else if a >= 6 && a < 7 { println("a >= 6 && a < 7") } else if a >= 7 && a < 10 { println("a >=7 && a < 10") } else { println("a > 10") } end rule "case2" "case2测试用例" salience 2 //先执行 begin if flag>0 { Print(flag+3) stag.StopTag = true successObj := success(222) successObj.AddDataParam("name2","test value") return successObj } end `
复制代码
上图中声明了规则, Print、success是咱们外部注入的方法, 咱们将在下一步制定.
rule表示一段新的规则开始 , 第一个为规则名(返回结果时候为对应key), 第二个为描述, 第三个 salience 表示规则的优先级
规则的优先级数字越大优先级越高(当使用 AsGivenSortedName 方法时, 会忽略掉规则内优先级做用 )
注入规则内依赖方法, 初始化规则池
apis := make(map[string]interface{})
apis["success"] = success
apis["Print"] = fmt.Println
var poolMinLen int64 = 50
var poolMaxLen int64 = 100
pool, err := engine.NewGenginePool(poolMinLen, poolMaxLen, engine.SortModel, rule1, apis)
if err != nil {
fmt.Println(err.Error())
return
}
复制代码
第一个参数 poolMinLen 表示初始化池最小50
第二个参数 poolMaxLen 表示初始化池最大100
第三个参数为设置执行模式(分为顺序执行、并发执行等), 只有调用 ExecuteSelectedRules 和 ExecuteSelectedRulesConcurrent 有效 (后续可作规则模式选择)
第四个参数是咱们配置的规则
第五个是咱们注入的变量值、方法等
调用执行规则
data := make(map[string]interface{})
data["flag"] = 2
stag := &engine.Stag{StopTag: false}
data["stag"] = stag //显示将标记注入进去
//_ = pool.RemoveRules([]string{"case1"}) //移除规则
/*_ = pool.UpdatePooledRulesIncremental(` //新增规则 rule "case66" "case66测试用例" salience 3 begin Print("case66") return false end `)*/
_, resp := pool.ExecuteSelectedRulesWithControlAndStopTag(data, true, stag, []string{"case1", "case66", "case2", "case0"})
fmt.Println(resp)
//output:
//5
//map[case2:{222 map[name2:test value]}]
复制代码
业务调用方变量注入, StopTag 声明为可中断, 执行选定的规则
层级架构图
业务场景经过业务编号(标识)调用规则接入层, 根据运行模式运行关联的规则.
接入层负责根据业务编号拿到具体规则进行运行, 同时接入层负责收集业务执行的异常与结果
最底层规则信息管理部分, 负责规则信息数据的维护与整理
业务接入模块分工流程
随着业务的快速发展, 代码的生命周期愈来愈短, 项目慢慢发展成为恐怖的“巨兽”, 吞噬着研发同窗的耐心, 叫苦连天却难以破局, 上面讲了这么多, 咱们怎么来判断是否适合引入规则引擎呢?
首先达成共识, 领导及产研同事是承认当下值得去作的, 能够解决掉咱们目前发现的痛点 (发现痛点能够先经过 最简单的方式先找出那些相似 if else多分支决策场景, 根据历史改动及业务需求来进行判断)
固然最好的话, 你对代码所服务的业务具备很好的预见能力.