golang規(guī)則引擎gengine用法案例
引言
本文受 golang面試經(jīng)典講解的[Go工具庫(kù)]B 站新一代 golang 規(guī)則引擎gengine啟示, 文中的基本用法和作者的細(xì)節(jié)都基本提到了,大家關(guān)心的可以去看下設(shè)計(jì)文檔及作者對(duì)比的gopher_lua的對(duì)比
本文主要下作者的幾個(gè)案例和用法
Part1 基本使用
package main
import (
"fmt"
"github.com/bilibili/gengine/builder"
"github.com/bilibili/gengine/context"
"github.com/bilibili/gengine/engine"
"github.com/sirupsen/logrus"
"time"
)
func main() {
TestMulti()
}
type User struct {
Name string
Age int64
Male bool
}
func (u *User) GetNum(i int64) int64 {
return i
}
func (u *User) Print(s string) {
fmt.Println(s)
}
func (u *User) Say() {
fmt.Println("hello world")
}
// 定義規(guī)則
const rule1 = `
rule "name test" "i can" salience 0
begin
if 7 == User.GetNum(7){
User.Age = User.GetNum(89767) + 10000000
User.Print("6666")
}else{
User.Name = "yyyy"
}
end
`
func TestMulti() {
user := &User{
Name: "Calo",
Age: 0,
Male: true,
}
dataContext := context.NewDataContext()
//注入初始化的結(jié)構(gòu)體
dataContext.Add("User", user)
//init rule engine
ruleBuilder := builder.NewRuleBuilder(dataContext)
start1 := time.Now()
//構(gòu)建規(guī)則
err := ruleBuilder.BuildRuleFromString(rule1) //string(bs)
end1 := time.Now()
logrus.Infof("rules num:%d, load rules cost time:%d", len(ruleBuilder.Kc.RuleEntities), end1.Sub(start1).Milliseconds())
if err != nil {
logrus.Errorf("err:%s ", err)
} else {
eng := engine.NewGengine()
start := time.Now().UnixNano()
//執(zhí)行規(guī)則
err := eng.Execute(ruleBuilder, true)
println(user.Age)
end := time.Now().UnixNano()
if err != nil {
logrus.Errorf("execute rule error: %v", err)
}
logrus.Infof("execute rule cost %d ns", end-start)
logrus.Infof("user.Age=%d,Name=%s,Male=%t", user.Age, user.Name, user.Male)
}
}INFO[0000] rules num:1, load rules cost time:5
6666
10089767
INFO[0000] execute rule cost 97000 ns
INFO[0000] user.Age=10089767,Name=Calo,Male=true
Part2 @name
在規(guī)則體中使用@name,指代的是當(dāng)前規(guī)則名,@name在規(guī)則執(zhí)行時(shí),會(huì)被解析為規(guī)則名字符串(規(guī)則內(nèi)的名字感知)
package main
import (
"fmt"
"github.com/bilibili/gengine/builder"
"github.com/bilibili/gengine/context"
"github.com/bilibili/gengine/engine"
"time"
)
func PrintName(name string) {
fmt.Println(name)
}
/*
*
use '@name',you can get rule name in rule content
*/
const atNameRule = `
rule "測(cè)試規(guī)則名稱1" "rule desc"
begin
va = @name
PrintName(va)
PrintName(@name)
end
rule "rule name" "rule desc"
begin
va = @name
PrintName(va)
PrintName(@name)
end
`
func exec() {
dataContext := context.NewDataContext()
dataContext.Add("PrintName", PrintName)
//init rule engine
ruleBuilder := builder.NewRuleBuilder(dataContext)
//resolve rules from string
start1 := time.Now().UnixNano()
err := ruleBuilder.BuildRuleFromString(atNameRule)
end1 := time.Now().UnixNano()
println(fmt.Sprintf("rules num:%d, load rules cost time:%d ns", len(ruleBuilder.Kc.RuleEntities), end1-start1))
if err != nil {
panic(err)
}
eng := engine.NewGengine()
start := time.Now().UnixNano()
// true: means when there are many rules, if one rule execute error,continue to execute rules after the occur error rule
err = eng.Execute(ruleBuilder, true)
end := time.Now().UnixNano()
if err != nil {
panic(err)
}
println(fmt.Sprintf("execute rule cost %d ns", end-start))
}
func main() {
exec()
}rules num:2, load rules cost time:2820000 ns
測(cè)試規(guī)則名稱1
測(cè)試規(guī)則名稱1
rule name
rule name
execute rule cost 72000 ns
@name 主要是獲取規(guī)則的名字的
Part3 @id
在規(guī)則體中使用@id含義是,如果當(dāng)前的規(guī)則名是可以轉(zhuǎn)化為整數(shù)的字符串,則@id就是規(guī)則名的整數(shù)值,如果規(guī)則名字符串不可轉(zhuǎn)化為整數(shù),則@id的值為0.這個(gè)是為了方便用戶以規(guī)則名作為整型參數(shù)
package main
import (
"fmt"
"github.com/bilibili/gengine/builder"
"github.com/bilibili/gengine/context"
"github.com/bilibili/gengine/engine"
)
/*
*
use '@id',you can get rule name in rule content
*/
const atIDRule = `
rule "測(cè)試規(guī)則名稱1" "rule desc" salience 10
begin
println(@id)
end
rule " 100 " "rule desc" salience 20
begin
x = @id
println(x)
end
`
func TestAtId() {
dataContext := context.NewDataContext()
dataContext.Add("println", fmt.Println)
//init rule engine
ruleBuilder := builder.NewRuleBuilder(dataContext)
//resolve rules from string
err := ruleBuilder.BuildRuleFromString(atIDRule)
if err != nil {
panic(err)
}
eng := engine.NewGengine()
err = eng.Execute(ruleBuilder, false)
if err != nil {
panic(err)
}
}
type Data struct {
M map[string]string
}
func (d *Data) exe() {
println("hhhh")
}
func main() {
TestAtId()
}100 0
Part4 @desc語法
在規(guī)則體內(nèi)獲知當(dāng)前規(guī)則的描述信息,類型為字符串
package main
import (
"fmt"
"github.com/bilibili/gengine/builder"
"github.com/bilibili/gengine/context"
"github.com/bilibili/gengine/engine"
"time"
)
/*
*
use '@desc',you can get rule description in rule content
*/
const atDescRule = `
rule "rule name 1" "我是一個(gè)測(cè)試用的描述信息1" salience 100
begin
desc = @desc
Print(desc)
Print(@name + " : " + @desc)
end
rule "rule name 2" //"我是描述,desc" salience 10
begin
desc = @desc
Print(desc)
Print(@name + " : " + @desc)
end
`
func main() {
dataContext := context.NewDataContext()
// dataContext.Add("Print", PrintName)
dataContext.Add("Print", fmt.Println)
//init rule engine
ruleBuilder := builder.NewRuleBuilder(dataContext)
//resolve rules from string
start1 := time.Now().UnixNano()
err := ruleBuilder.BuildRuleFromString(atDescRule)
end1 := time.Now().UnixNano()
println(fmt.Sprintf("rules num:%d, load rules cost time:%d ns", len(ruleBuilder.Kc.RuleEntities), end1-start1))
if err != nil {
panic(err)
}
eng := engine.NewGengine()
// true: means when there are many rules, if one rule execute error,continue to execute rules after the occur error rule
err = eng.Execute(ruleBuilder, true)
if err != nil {
panic(err)
}
}rules num:2, load rules cost time:3172000 ns
我是一個(gè)測(cè)試用的描述信息1
rule name 1 : 我是一個(gè)測(cè)試用的描述信息1rule name 2 :
注意如果有//是注釋 無法解析到
Part5 sal
在規(guī)則體內(nèi)獲知當(dāng)前規(guī)則的優(yōu)先級(jí)信息,類型為int64
package main
import (
"fmt"
"github.com/bilibili/gengine/builder"
"github.com/bilibili/gengine/context"
"github.com/bilibili/gengine/engine"
"time"
)
func main() {
dataContext := context.NewDataContext()
dataContext.Add("println", fmt.Println)
//init rule engine
ruleBuilder := builder.NewRuleBuilder(dataContext)
err := ruleBuilder.BuildRuleFromString(`
rule "1" salience 10
begin
println(@sal)
end
rule "2"
begin
println(@sal)
end
`)
if err != nil {
panic(err)
}
eng := engine.NewGengine()
start := time.Now().UnixNano()
// true: means when there are many rules, if one rule execute error,continue to execute rules after the occur error rule
err = eng.Execute(ruleBuilder, true)
end := time.Now().UnixNano()
if err != nil {
panic(err)
}
println(fmt.Sprintf("execute rule cost %d ns", end-start))
}10 0 execute rule cost 120000 ns
Part6 注釋
支持規(guī)則內(nèi)的單行注釋,注釋以雙斜杠(//)開頭
Part7 自定義變量
用戶自定義變量無需申明類型 規(guī)則內(nèi)定義的變量,只對(duì)當(dāng)前規(guī)則可見,對(duì)其他規(guī)則不可見(局部變量) 使用dataContext注入的(變量)數(shù)據(jù),對(duì)加載到gengine中的所有規(guī)則均可見(全局變量)
Part8 報(bào)錯(cuò)時(shí)行號(hào)提示
gengine支持的語法,是完整的DSL語法(也可以當(dāng)作是一門完整的語言),gengine規(guī)則執(zhí)行出錯(cuò)時(shí),gengine會(huì)指出具體的出錯(cuò)在哪一行.盡量幫助用戶在使用gengine的每一個(gè)流程細(xì)節(jié)上,都有絲滑體驗(yàn)
package main
import (
"fmt"
"github.com/bilibili/gengine/builder"
"github.com/bilibili/gengine/context"
"github.com/bilibili/gengine/engine"
)
var lineNumberRules = `
rule "line_number" "when execute error,gengine will give out error"
begin
//Println("golang", "hello", "world" )
//取消斜杠注釋,依次測(cè)試不同的報(bào)錯(cuò)情況
//if Println("golang", "hello") == 100 {
// Println("golang", "hello")
//}
ms.X()
end
`
type MyStruct struct {
}
func (m *MyStruct) XX(s string) {
println("XX")
}
func Println(s1, s2 string) bool {
println(s1, s2)
return false
}
func main() {
dataContext := context.NewDataContext()
//注入自定義函數(shù)
dataContext.Add("Println", Println)
ms := &MyStruct{}
dataContext.Add("ms", ms)
ruleBuilder := builder.NewRuleBuilder(dataContext)
e1 := ruleBuilder.BuildRuleFromString(lineNumberRules)
if e1 != nil {
panic(e1)
}
eng := engine.NewGengine()
// true: means when there are many rules, if one rule execute error,continue to execute rules after the occur error rule
e2 := eng.Execute(ruleBuilder, true)
if e2 != nil {
println(fmt.Sprintf("%+v", e2))
}
}[rule: "line_number" executed, error:
line 12, column 0, code: ms.X(), NOT FOUND Function: X ]
Part9 多級(jí)調(diào)用支持
現(xiàn)在已經(jīng)支持了A.B.C形式的三級(jí)調(diào)用,但三級(jí)以上的調(diào)用則不支持 其他細(xì)節(jié)說明:
1.當(dāng)語法是C,或者a=C, 則C可以為具體值、變量、函數(shù)或方法、結(jié)構(gòu)體(指針)、map、slice、array等, 具體的如 a=100, a = Mp["hello"], a = x, a = getMessage(p1,p2..)等
2.當(dāng)語法是A.C,或者a=A.C, 則A必須為結(jié)構(gòu)體(指針), C同上, 具體如a = A.Mp["hello"], a = A.Field1, a = A.GetMessage(p1,p2..)等
3.當(dāng)語法是A.B.C,或者a=A.B.C, 則A和B必須為結(jié)構(gòu)體(指針),C同上, 具體如 a = A.B.Mp["hello"], a =A.B.Field1, a= A.B.GetMessage(p1, p2..)等
那么,如果語法為A.B.C時(shí), A.Mp["hello"].C 這種語法是不合法的
package main
import (
"fmt"
"github.com/bilibili/gengine/builder"
"github.com/bilibili/gengine/context"
"github.com/bilibili/gengine/engine"
)
type B struct {
Name string
Mp map[string]string
Ar [5]string
Sl []int
}
func (b *B) Meth(s string) {
println("--->", s)
}
func main() {
type A struct {
N string
Ma map[string]string
B *B
}
rule := `
rule "three level call"
begin
conc{
A.B.Name = "xiaoMing"
A.B.Mp["hello"] = "world"
A.B.Ar[1] = "Calo"
A.B.Sl[2] = 3
x = A.B.Sl[0]
A.B.Meth(A.B.Ar[1])
A.N = "kakaka"
A.Ma["a"] = "b"
}
println(A.B.Name, A.B.Mp["hello"], A.B.Ar[1], A.B.Sl[2], x, A.N, A.Ma["a"])
if A.B.Sl[0] == 0 {
println(true)
}
end
`
b := B{
Name: "",
Mp: make(map[string]string),
Ar: [5]string{},
Sl: make([]int, 6),
}
pA := &A{
N: "",
Ma: make(map[string]string),
B: &b,
}
dataContext := context.NewDataContext()
dataContext.Add("println", fmt.Println)
dataContext.Add("A", pA)
ruleBuilder := builder.NewRuleBuilder(dataContext)
e := ruleBuilder.BuildRuleFromString(rule)
if e != nil {
panic(e)
}
gengine := engine.NewGengine()
e = gengine.Execute(ruleBuilder, true)
if e != nil {
panic(e)
}
println(pA.B.Name, pA.B.Mp["hello"], pA.B.Ar[1], pA.B.Sl[2])
}---> Calo
xiaoMing world Calo 3 0 kakaka b
true
xiaoMing world Calo 3
以上就是golang 規(guī)則引擎gengine用法案例的詳細(xì)內(nèi)容,更多關(guān)于golang 規(guī)則引擎gengine的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
GoFrame框架數(shù)據(jù)校驗(yàn)之校驗(yàn)對(duì)象校驗(yàn)結(jié)構(gòu)體
這篇文章主要為大家介紹了GoFrame框架數(shù)據(jù)校驗(yàn)之校驗(yàn)對(duì)象校驗(yàn)結(jié)構(gòu)體示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-06-06
詳解Golang中創(chuàng)建error的方式總結(jié)與應(yīng)用場(chǎng)景
Golang中創(chuàng)建error的方式包括errors.New、fmt.Errorf、自定義實(shí)現(xiàn)了error接口的類型等,本文主要為大家介紹了這些方式的具體應(yīng)用場(chǎng)景,需要的可以參考一下2023-07-07
Go語言web快速開發(fā)框架Gin的HttpRouter路由的使用
in框架內(nèi)部使用了高性能的路由器庫(kù)httprouter,支持動(dòng)態(tài)參數(shù)匹配和簡(jiǎn)潔的接口,本文主要介紹了Go語言web快速開發(fā)框架Gin的HttpRouter路由的使用,感興趣的可以了解一下2025-03-03

