B站新一代 golang规则引擎gengine基础语法

 更新时间:2023年12月11日 10:41:46   作者:萧楚河  
这篇文章主要为大家介绍了B站新一代 golang规则引擎gengine基础语法,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

前言

  • gengine是一款基于golang和AST(抽象语法树)开发的规则引擎,gengine支持的语法是一种自定义的DSL

  • gengine于2020年7月由哔哩哔哩(bilibili.com)授权开源

  • gengine现已应用于B站风控系统、流量投放系统、AB测试、推荐平台系统等多个业务场景

  • 你也可以将gengine应用于golang应用的任何需要规则或指标支持的业务场景

优势

对比droolsgengine
执行模式仅支持顺序模式支持顺序模式、并发模式、混合模式,以及其他细分执行模式
规则编写难易程度高,与java强相关低,自定义简单语法,与golang弱相关
规则执行性能低、无论是规则之间还是规则内部,都是顺序执行高,无论是规则间、还是规则内,都支持并发执行.用户基于需要来选择合适的执行模式

开源代码地址

https://github.com/bilibili/gengine

https://github.com/bilibili/gengine

语法

DSL语法

const rule = `
rule "rulename" "rule-describtion" salience  10
begin
//规则体
end`

如上,gengine DSL完整的语法块由如下几个组件构成:

  • 关键字rule,之后紧跟"规则名称"和"规则描述",规则名称是必须的,但规则描述不是必须的. 当一个gengine实例中有多个规则时,"规则名"必须唯一,否则当有多个相同规则名的规则时,编译好之后只会存在一个

  • 关键字salience,之后紧跟一个整数,表示的规则优先级,它们为非必须.数字越大,规则优先级越高;当用户没有显式的指明优先级时,规则的优先级未知, 如果多个规则的优先级相同,那么在执行的时候,相同优先级的规则执行顺序未知

  • 关键字begin和end包裹的是规则体,也就是规则的具体逻辑

规则体语法

  • 规则体的语法支持或执行顺序,与主流的计算计语言(如golang、java、C/C++等)一致

规则体支持的运算

  • 支持完整数值之间的加(+)、减(-)、乘(*)、除(/)四则运算,以及字符串之间的加法

  • 完整的逻辑运算(&&、 ||、 !)

  • 支持比较运算符: 等于(==)、不等于(!=)、大于(>)、小于(<)、大于等于(>=)、小于等于(<=)

  • 支持+=, -=, *=, /=

  • 支持小括号

  • 优先级:括号, 非, 乘除, 加减, 逻辑运算(&&,||) 依次降低

规则体支持的基础数据类型

  • string

  • bool

  • int, int8, int16, int32, int64

  • uint, uint8, uint16,uint32, uint64

  • float32, float64

不支持的特例

  • 不支持直接处理nil,但用户可以在rule中定义一个变量去接受nil,然后再定义一个函数去处理nil

  • 为了用户使用方便,最新版gengine已经内置了isNil()函数,用户可以直接使用,用于判断数据是否为nil

规则体支持的语法

  • 完整的if .. else if .. else 语法结构,及其嵌套结构

其他语法大家如有需求可以去官方文档查看:

https://github.com/bilibili/gengine/wiki/%E8%AF%AD%E6%B3%95

使用案例

package test
import (
  "bytes"
  "fmt"
  "github.com/bilibili/gengine/builder"
  "github.com/bilibili/gengine/context"
  "github.com/bilibili/gengine/engine"
  "github.com/sirupsen/logrus"
  "io/ioutil"
  "strconv"
  "strings"
  "testing"
  "time"
)
//定义想要注入的结构体
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")
}
//定义规则
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 Test_Multi(t *testing.T){
  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().UnixNano()
    //构建规则
  err := ruleBuilder.BuildRuleFromString(rule1) //string(bs)
  end1 := time.Now().UnixNano()
  logrus.Infof("rules num:%d, load rules cost time:%d", len(ruleBuilder.Kc.RuleEntities), end1-start1 )
  if err != nil{
    logrus.Errorf("err:%s ", err)
  }else{
    eng := engine.NewGengine()
    start := time.Now().UnixNano()
        //执行规则
    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)
  }
}

示例解释

  • User是需要被注入到gengine中的结构体;结构体在注入之前需要被初始化;结构体需要以指针的形式被注入,否则无法在规则中改变其属性值

  • rule1是以字符串定义的具体规则

  • dataContext用于接受注入的数据(结构体、方法等)

  • ruleBuilder用于编译字符串形式的规则

  • engine接受ruleBuilder,并以用户选定的执行模式执行加载好的规则

小技巧

  • 通过示例可以发现,规则的编译构建和执行是异步的. 因此,用户可使用此特性,在不停服的情况下进行更新规则.

  • 需要注意的是,编译构建规则是一个CPU密集型的事情,通常只有规则被用户更新的时候才去编译构建更新;

  • gengine内部针对规则加载与移除已经做了很多的优化,gengine pool提供的所有相关的api也是线程安全且高性能的,因此我们建议您直接使用gengine pool

  • 另外,用户还可以通过ruleBuilder来进行异步的语法检测.

如果大家对设计实现比较感兴趣可以通过如下地址查看:

//www.jb51.net/jiaoben/284935hzu.htm

以上就是B站新一代 golang规则引擎gengine基础语法的详细内容,更多关于B站go规则引擎gengine的资料请关注脚本之家其它相关文章!

相关文章

  • 聊聊Golang中很好用的viper配置模块

    聊聊Golang中很好用的viper配置模块

    这篇文章主要介绍了Golang中很好用的viper配置模块用法,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-12-12
  • Go语言中多字节字符的处理方法详解

    Go语言中多字节字符的处理方法详解

    这篇文章主要给大家介绍了关于Go语言中多字节字符的处理方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2018-10-10
  • Go 实现百万WebSocket连接的方法示例

    Go 实现百万WebSocket连接的方法示例

    这篇文章主要介绍了Go 实现百万WebSocket连接的方法示例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-08-08
  • Go语言中循环Loop的用法介绍

    Go语言中循环Loop的用法介绍

    这篇文章介绍了Go语言中循环Loop的用法,文中通过示例代码介绍的非常详细。对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2022-07-07
  • 基于Go语言实现类似tree命令的小程序

    基于Go语言实现类似tree命令的小程序

    tree 命令是一个小型的跨平台命令行程序,用于递归地以树状格式列出或显示目录的内容。本文将通过Go语言实现类似tree命令的小程序,需要的可以参考一下
    2022-10-10
  • Golang 空map和未初始化map的注意事项说明

    Golang 空map和未初始化map的注意事项说明

    这篇文章主要介绍了Golang 空map和未初始化map的注意事项说明,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2021-04-04
  • Go语言单元测试基础从入门到放弃

    Go语言单元测试基础从入门到放弃

    这篇文章主要介绍了Go单元测试基础从入门到放弃为大家开启Go语言单元测试第一篇章,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-06-06
  • Go 1.22对net/http包的路由增强功能详解

    Go 1.22对net/http包的路由增强功能详解

    Go 1.22 版本对 net/http 包的路由功能进行了增强,引入了方法匹配(method matching)和通配符(wildcards)两项新功能,本文将给大家详细的介绍一下Go 1.22对net/http包的路由增强功能,需要的朋友可以参考下
    2024-02-02
  • golang生成指定位数的随机数的方法

    golang生成指定位数的随机数的方法

    这篇文章主要介绍了golang生成指定位数的随机数的方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-10-10
  • Golang中slice删除元素的性能对比

    Golang中slice删除元素的性能对比

    go没有对删除切片元素提供专用的语法或者接口,需要使用切片本身的特性来删除元素,下面这篇文章主要给大家介绍了关于Golang中slice删除元素的性能对比,需要的朋友可以参考下
    2022-06-06

最新评论