golang高性能表达式求值引擎插件expr的使用

Golang高性能表达式求值引擎插件Expr的使用

Expr是一个Go语言为中心设计的表达式语言,旨在提供动态配置功能,具有无与伦比的准确性、安全性和速度。

主要特性

  • 内存安全:设计注重安全性,确保程序不会访问无关内存或引入内存漏洞
  • 无副作用:表达式评估只计算输入输出,确保不会产生副作用
  • 静态类型:确保类型正确性,防止运行时类型错误
  • 高性能:采用优化编译器和字节码虚拟机
  • 丰富的内置函数:提供all、none、any、one、filter和map等函数

安装

go get github.com/expr-lang/expr

使用示例

基础示例

package main

import (
	"fmt"
	"github.com/expr-lang/expr"
)

func main() {
	env := map[string]interface{}{
		"greet":   "Hello, %v!",
		"names":   []string{"world", "you"},
		"sprintf": fmt.Sprintf,
	}

	code := `sprintf(greet, names[0])`

	program, err := expr.Compile(code, expr.Env(env))
	if err != nil {
		panic(err)
	}

	output, err := expr.Run(program, env)
	if err != nil {
		panic(err)
	}

	fmt.Println(output)  // 输出: Hello, world!
}

结构体示例

package main

import (
	"fmt"
	"github.com/expr-lang/expr"
)

type Tweet struct {
	Len int
}

type Env struct {
	Tweets []Tweet
}

func main() {
	code := `all(Tweets, {.Len <= 240})`

	program, err := expr.Compile(code, expr.Env(Env{}))
	if err != nil {
		panic(err)
	}

	env := Env{
		Tweets: []Tweet{{42}, {98}, {69}},
	}
	output, err := expr.Run(program, env)
	if err != nil {
		panic(err)
	}

	fmt.Println(output)  // 输出: true
}

表达式示例

Expr支持多种表达式语法:

// 只允许管理员和版主审核评论
user.Group in ["admin", "moderator"] || user.Id == comment.UserId

// 判断请求是否在允许的时间窗口内
request.Time - resource.Age < duration("24h")

// 确保所有推文不超过240个字符
all(tweets, len(.Content) <= 240)

类型检查

Expr会在编译时进行类型检查:

out, err := expr.Compile(`name + age`)
// err: invalid operation + (mismatched types string and int)
// | name + age
// | .....^

使用场景

Expr适用于各种需要动态表达式求值的场景,如:

  • 业务规则引擎
  • 动态配置
  • 条件过滤
  • 权限控制
  • 数据验证

Expr已被Google、Uber、GoDaddy等公司用于生产环境。


更多关于golang高性能表达式求值引擎插件expr的使用的实战教程也可以访问 https://www.itying.com/category-94-b0.html

1 回复

更多关于golang高性能表达式求值引擎插件expr的使用的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


Go高性能表达式求值引擎:expr

expr 是一个高性能的表达式求值引擎,专为 Go 语言设计。它提供了简单易用的 API 来解析和求值表达式,支持自定义函数和变量,非常适合配置化业务逻辑、规则引擎等场景。

主要特性

  • 高性能:expr 经过优化,执行速度快
  • 类型安全:编译时检查表达式类型
  • 简单易用:直观的 API 设计
  • 可扩展:支持自定义函数和变量
  • 安全:默认限制潜在危险操作

基本使用

安装

go get github.com/antonmedv/expr

简单示例

package main

import (
	"fmt"
	"github.com/antonmedv/expr"
)

func main() {
	// 最简单的表达式求值
	result, err := expr.Eval("1 + 2", nil)
	if err != nil {
		panic(err)
	}
	fmt.Println(result) // 输出: 3

	// 使用环境变量
	env := map[string]interface{}{
		"name":   "Alice",
		"age":    25,
		"adult":  true,
		"prices": []float64{10.5, 20.3, 30.1},
	}

	// 带变量的表达式
	code := `name + " is " + age + " years old"`
	result, err = expr.Eval(code, env)
	if err != nil {
		panic(err)
	}
	fmt.Println(result) // 输出: Alice is 25 years old

	// 条件表达式
	code = `age >= 18 && adult ? "Adult" : "Minor"`
	result, err = expr.Eval(code, env)
	if err != nil {
		panic(err)
	}
	fmt.Println(result) // 输出: Adult

	// 数组操作
	code = `sum(prices)`
	result, err = expr.Eval(code, env)
	if err != nil {
		panic(err)
	}
	fmt.Println(result) // 输出: 60.9
}

高级用法

预编译表达式

对于需要多次执行的表达式,可以先编译再执行以提高性能:

program, err := expr.Compile(`price > 100 && in_stock`, expr.Env(map[string]interface{}{
	"price":    0,
	"in_stock": false,
}))
if err != nil {
	panic(err)
}

// 多次执行
for _, product := range products {
	result, err := expr.Run(program, map[string]interface{}{
		"price":    product.Price,
		"in_stock": product.InStock,
	})
	if err != nil {
		panic(err)
	}
	if result.(bool) {
		fmt.Println("High value product:", product.Name)
	}
}

自定义函数

env := map[string]interface{}{
	"greet": func(name string) string {
		return "Hello, " + name + "!"
	},
	"names": []string{"Alice", "Bob", "Charlie"},
}

code := `map(names, greet)`
result, err := expr.Eval(code, env)
if err != nil {
	panic(err)
}
fmt.Println(result) 
// 输出: [Hello, Alice!, Hello, Bob!, Hello, Charlie!]

结构体环境

expr 也支持使用结构体作为环境:

type Env struct {
	User struct {
		Name string
		Age  int
	}
	Discount float64
}

program, err := expr.Compile(
	`User.Age >= 18 && Discount > 0.1`,
	expr.Env(Env{}),
)
if err != nil {
	panic(err)
}

env := Env{
	User: struct {
		Name string
		Age  int
	}{
		Name: "Alice",
		Age:  25,
	},
	Discount: 0.15,
}

output, err := expr.Run(program, env)
if err != nil {
	panic(err)
}
fmt.Println(output) // 输出: true

性能优化技巧

  1. 预编译表达式:对于重复执行的表达式,先编译再执行
  2. 重用环境对象:避免频繁创建环境对象
  3. 限制表达式复杂度:对于不可信输入,限制表达式复杂度
  4. 使用简单类型:基本类型比复杂类型处理更快

安全注意事项

expr 默认是安全的,但如果你需要执行不可信输入,可以添加限制:

program, err := expr.Compile(input, expr.Env(env), expr.AllowUndefinedVariables())

expr 提供了多种选项来控制允许的操作:

  • AllowUndefinedVariables() - 允许未定义变量
  • AllowUndefinedFunctions() - 允许未定义函数
  • AllowBuiltins() - 允许内置函数
  • AllowAll() - 允许所有操作(不推荐)

表达式语法

expr 支持丰富的表达式语法:

  • 算术运算: +, -, *, /, %, **
  • 比较运算: ==, !=, <, <=, >, >=
  • 逻辑运算: &&, ||, !
  • 条件表达式: a ? b : c
  • 数组操作: filter, map, reduce, all, any, none, one
  • 字符串操作: contains, startsWith, endsWith, matches
  • 其他: in, .. (范围), [] (索引)

实际应用场景

  1. 业务规则引擎:将业务规则配置为表达式
  2. 数据过滤:动态过滤数据集
  3. 计算字段:动态计算字段值
  4. 条件触发:根据条件触发不同操作
  5. 配置化逻辑:将业务逻辑配置化而非硬编码

expr 是一个强大而灵活的工具,可以帮助你将业务逻辑从代码中解耦出来,实现更高程度的配置化和灵活性。

回到顶部