golang規(guī)則引擎gengine用法案例
引言
本文受 golang面試經(jīng)典講解
的[Go工具庫]B 站新一代 golang 規(guī)則引擎gengine啟示, 文中的基本用法和作者的細節(jié)都基本提到了,大家關心的可以去看下設計文檔及作者對比的gopher_lua
的對比
本文主要下作者的幾個案例和用法
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() //注入初始化的結構體 dataContext.Add("User", user) //init rule engine ruleBuilder := builder.NewRuleBuilder(dataContext) start1 := time.Now() //構建規(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,指代的是當前規(guī)則名,@name在規(guī)則執(zhí)行時,會被解析為規(guī)則名字符串(規(guī)則內的名字感知)
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 "測試規(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
測試規(guī)則名稱1
測試規(guī)則名稱1
rule name
rule name
execute rule cost 72000 ns
@name 主要是獲取規(guī)則的名字的
Part3 @id
在規(guī)則體中使用@id含義是,如果當前的規(guī)則名是可以轉化為整數(shù)的字符串,則@id就是規(guī)則名的整數(shù)值,如果規(guī)則名字符串不可轉化為整數(shù),則@id的值為0.這個是為了方便用戶以規(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 "測試規(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ī)則體內獲知當前規(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" "我是一個測試用的描述信息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
我是一個測試用的描述信息1
rule name 1 : 我是一個測試用的描述信息1rule name 2 :
注意如果有//是注釋 無法解析到
Part5 sal
在規(guī)則體內獲知當前規(guī)則的優(yōu)先級信息,類型為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ī)則內的單行注釋,注釋以雙斜杠(//)開頭
Part7 自定義變量
用戶自定義變量無需申明類型 規(guī)則內定義的變量,只對當前規(guī)則可見,對其他規(guī)則不可見(局部變量) 使用dataContext注入的(變量)數(shù)據(jù),對加載到gengine中的所有規(guī)則均可見(全局變量)
Part8 報錯時行號提示
gengine支持的語法,是完整的DSL語法(也可以當作是一門完整的語言),gengine規(guī)則執(zhí)行出錯時,gengine會指出具體的出錯在哪一行.盡量幫助用戶在使用gengine的每一個流程細節(jié)上,都有絲滑體驗
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" ) //取消斜杠注釋,依次測試不同的報錯情況 //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 多級調用支持
現(xiàn)在已經(jīng)支持了A.B.C形式的三級調用,但三級以上的調用則不支持 其他細節(jié)說明:
1.當語法是C,或者a=C, 則C可以為具體值、變量、函數(shù)或方法、結構體(指針)、map、slice、array等, 具體的如 a=100, a = Mp["hello"], a = x, a = getMessage(p1,p2..)等
2.當語法是A.C,或者a=A.C, 則A必須為結構體(指針), C同上, 具體如a = A.Mp["hello"], a = A.Field1, a = A.GetMessage(p1,p2..)等
3.當語法是A.B.C,或者a=A.B.C, 則A和B必須為結構體(指針),C同上, 具體如 a = A.B.Mp["hello"], a =A.B.Field1, a= A.B.GetMessage(p1, p2..)等
那么,如果語法為A.B.C時, 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用法案例的詳細內容,更多關于golang 規(guī)則引擎gengine的資料請關注腳本之家其它相關文章!
相關文章
GoFrame框架數(shù)據(jù)校驗之校驗對象校驗結構體
這篇文章主要為大家介紹了GoFrame框架數(shù)據(jù)校驗之校驗對象校驗結構體示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-06-06詳解Golang中創(chuàng)建error的方式總結與應用場景
Golang中創(chuàng)建error的方式包括errors.New、fmt.Errorf、自定義實現(xiàn)了error接口的類型等,本文主要為大家介紹了這些方式的具體應用場景,需要的可以參考一下2023-07-07Go語言web快速開發(fā)框架Gin的HttpRouter路由的使用
in框架內部使用了高性能的路由器庫httprouter,支持動態(tài)參數(shù)匹配和簡潔的接口,本文主要介紹了Go語言web快速開發(fā)框架Gin的HttpRouter路由的使用,感興趣的可以了解一下2025-03-03