最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

在golang中使用cel的用法詳解

 更新時(shí)間:2023年11月07日 08:46:15   作者:demo007x  
CEL?是一種非圖靈完備的表達(dá)式語言?,旨在快速、可移植且執(zhí)行安全,CEL?可以單獨(dú)使用,也可以嵌入到其他的產(chǎn)品中,本文將給大家介紹一下golang中如何使用cel,需要的朋友可以參考下

什么是CEL?

CEL 是一種非圖靈完備的表達(dá)式語言 ,旨在快速、可移植且執(zhí)行安全。CEL 可以單獨(dú)使用,也可以嵌入到其他的產(chǎn)品中。

CEL被設(shè)計(jì)成一種可以安全地執(zhí)行用戶代碼的語言。雖然盲目調(diào)用用戶的python代碼是危險(xiǎn)的,但您可以安全地執(zhí)行用戶的CEL代碼。由于CEL防止了會(huì)降低其性能的行為,因此它的評(píng)估安全性在納秒到微秒之間;它非常適合性能關(guān)鍵型應(yīng)用程序。eval()

CEL 計(jì)算表達(dá)式,類似于單行函數(shù)或 lambda 表達(dá)式。雖然 CEL 通常用于布爾決策,但它也可用于構(gòu)造更復(fù)雜的對(duì)象,如 JSONprotobuf 消息。

關(guān)鍵概念

應(yīng)用

CEL是通用的,已用于各種應(yīng)用程序,從路由RPC到定義安全策略。CEL是可擴(kuò)展的,與應(yīng)用程序無關(guān),并針對(duì)一次編譯、多次評(píng)估的工作流進(jìn)行了優(yōu)化。 許多服務(wù)和應(yīng)用程序評(píng)估聲明性配置。例如,基于角色的訪問控制(RBAC)是一種聲明性配置,它在給定角色和一組用戶的情況下生成訪問決策。如果聲明性配置是80%的用例,那么當(dāng)用戶需要更強(qiáng)的表達(dá)能力時(shí),CEL是一個(gè)有用的工具,可以將剩余的20%取整。

編譯

表達(dá)式是針對(duì)環(huán)境編譯的。編譯步驟生成protobuf 形式的抽象語法樹(AST)。編譯后的表達(dá)式通常會(huì)存儲(chǔ)起來以備將來使用,從而使求值盡可能快。單個(gè)編譯表達(dá)式可以使用許多不同的輸入進(jìn)行求值。

抽象語法樹:

在計(jì)算機(jī)科學(xué)中,抽象語法樹Abstract Syntax Tree,AST),或簡(jiǎn)稱語法樹(Syntax tree),是源代碼語法結(jié)構(gòu)的一種抽象表示。它以樹狀的形式表現(xiàn)編程語言的語法結(jié)構(gòu),樹上的每個(gè)節(jié)點(diǎn)都表示源代碼中的一種結(jié)構(gòu)。之所以說語法是“抽象”的,是因?yàn)檫@里的語法并不會(huì)表示出真實(shí)語法中出現(xiàn)的每個(gè)細(xì)節(jié)。比如,嵌套括號(hào)被隱含在樹的結(jié)構(gòu)中,并沒有以節(jié)點(diǎn)的形式呈現(xiàn);而類似于 if-condition-then 這樣的條件跳轉(zhuǎn)語句,可以使用帶有三個(gè)分支的節(jié)點(diǎn)來表示。

和抽象語法樹相對(duì)的是具體語法樹(通常稱作分析樹)。一般的,在源代碼的翻譯和編譯過程中,語法分析器創(chuàng)建出分析樹,然后從分析樹生成AST。一旦AST被創(chuàng)建出來,在后續(xù)的處理過程中,比如語義分析階段,會(huì)添加一些信息。

表達(dá)式

用戶定義表達(dá)式;服務(wù)和應(yīng)用程序定義了它運(yùn)行的環(huán)境。函數(shù)簽名聲明輸入,并在CEL表達(dá)式之外編寫。CEL 可用的函數(shù)庫是自動(dòng)導(dǎo)入的。 在下面的示例中,表達(dá)式采用一個(gè)請(qǐng)求對(duì)象,該請(qǐng)求包括一個(gè)聲明令牌。該表達(dá)式返回一個(gè)布爾值,指示聲明令牌是否仍然有效。

// 通過檢查“exp”聲明來檢查JSON Web令牌是否已過期。
//
// Args:
//   claims - authentication claims.
//   now    - timestamp indicating the current system time.
// 如果令牌已過期,則返回:true
//
timestamp(claims["exp"]) < now

環(huán)境

環(huán)境是由服務(wù)定義的。嵌入CEL的服務(wù)和應(yīng)用程序聲明表達(dá)式環(huán)境。環(huán)境是可以在表達(dá)式中使用的變量和函數(shù)的集合。 CEL 類型檢查器使用基于原型的聲明來確保表達(dá)式中的所有標(biāo)識(shí)符和函數(shù)引用都得到了正確的聲明和使用。

解析表達(dá)式的三個(gè)階段

處理表達(dá)式有三個(gè)階段:解析檢查求值 。CEL最常見的模式是控制平面在配置時(shí)解析和檢查表達(dá)式,并存儲(chǔ)AST

在運(yùn)行時(shí),數(shù)據(jù)平面會(huì)重復(fù)檢索和評(píng)估AST。CEL 針對(duì)運(yùn)行時(shí)效率進(jìn)行了優(yōu)化,但不應(yīng)在延遲關(guān)鍵的代碼路徑中進(jìn)行解析和檢查。

CEL使用ANTLR lexer/parser語法從人類可讀的表達(dá)式解析為抽象語法樹。解析階段發(fā)出一個(gè)基于原型的抽象語法樹,其中AST中的每個(gè)Expr節(jié)點(diǎn)都包含一個(gè)整數(shù)id,用于索引解析和檢查期間生成的元數(shù)據(jù)。解析過程中生成的syntax.proto忠實(shí)地表示了以字符串形式鍵入的內(nèi)容的抽象表示。

一旦解析了表達(dá)式,就可以根據(jù)環(huán)境對(duì)其進(jìn)行檢查,以確保表達(dá)式中的所有變量和函數(shù)標(biāo)識(shí)符都已聲明并正確使用。類型檢查器生成一個(gè)checked.proto,其中包括類型、變量和函數(shù)解析元數(shù)據(jù),可以顯著提高評(píng)估效率。

評(píng)估CEL需要3件事:

  • 任何自定義擴(kuò)展的函數(shù)綁定

  • 變量綁定

  • AST評(píng)估

函數(shù)和變量綁定應(yīng)該與用于編譯AST的綁定相匹配。這些輸入中的任何一個(gè)都可以在多個(gè)評(píng)估中重復(fù)使用,例如在多組變量綁定中評(píng)估AST,或者在多個(gè)AST中使用相同的變量,或者在進(jìn)程的整個(gè)生命周期中使用函數(shù)綁定(常見情況)。

CEL 適合您的項(xiàng)目嗎?

由于 CEL 以納秒到微秒為單位評(píng)估 AST 的表達(dá)式,因此 CEL 的理想用例是具有性能關(guān)鍵路徑的應(yīng)用程序。不應(yīng)在關(guān)鍵路徑中將 CEL 代碼編譯到 AST 中;理想的應(yīng)用程序是經(jīng)常執(zhí)行配置且修改頻率相對(duì)較低的應(yīng)用程序。

例如,對(duì)服務(wù)的每個(gè) HTTP 請(qǐng)求執(zhí)行安全策略是 CEL 的理想用例,因?yàn)榘踩呗院苌俑?,并?CEL 對(duì)響應(yīng)時(shí)間的影響可以忽略不計(jì)。在這種情況下,CEL 將返回一個(gè)布爾值(無論是否允許該請(qǐng)求),但它可能會(huì)返回更復(fù)雜的消息。

在 golang 中如何使用 CEL

一下代碼我們使用 golang 的 cel 包 github.com/google/cel-go/cel

使用 cel 進(jìn)行字符串拼接:

字符串 str = "Hello world! I'm " + name + "."中存在變量 name ,在我們的程序中,這個(gè) name 是一個(gè)變量,需要在程序中替換為具體的值,比如:張三

步驟如下:

1、先初始化 env,也就是我們上面說的需要配置執(zhí)行的環(huán)境

2、在環(huán)境中綁定變量 name 以及類型

3、env.Compile(str) 就是做了我們上面所說的編譯并解析表達(dá)式,返回 str 所對(duì)應(yīng)的ast

4、程序求值。我們將 name 需要的具體值傳到 program 中執(zhí)行 values := map[string]interface{}{"name": "CEL"}

5、獲取到最后的結(jié)果

func Test_exprReplacement(t *testing.T) {
    var str = `"Hello world! I'm " + name + "."`
    env, err := cel.NewEnv(
        cel.Variable("name", cel.StringType), // 參數(shù)類型綁定
    )
    if err != nil {
        t.Fatal(err)
    }
    ast, iss := env.Compile(str) // 編譯,校驗(yàn),執(zhí)行 str
    if iss.Err() != nil {
        t.Fatal(iss.Err())
    }
    program, err := env.Program(ast)
    if err != nil {
        t.Fatal(err)
    }
  // 初始化 name 變量的值
    values := map[string]interface{}{"name": "CEL"}
  // 傳給內(nèi)部程序并返回執(zhí)行的結(jié)果
    out, detail, err := program.Eval(values)
    if err != nil {
        t.Fatal(err)
    }
    fmt.Println(detail)
    fmt.Println(out)
}

測(cè)試結(jié)果:

Running tool: /usr/local/go/bin/go test -timeout 10s -run ^Test_exprReplacement$ github.com/demo007x/goexpr -v

=== RUN   Test_exprReplacement
Hello world! I'm CEL.
<nil>
--- PASS: Test_exprReplacement (0.00s)
PASS
ok      github.com/demo007x/goexpr    0.010s

計(jì)算一個(gè)表達(dá)式的邏輯結(jié)果:

返回表達(dá)式 var str = 100 + 200 >= 300 的執(zhí)行結(jié)果:

執(zhí)行步驟跟上面的一樣,這里就省略了。

func Test_LogicExpr1(t *testing.T) {
    var str = `100 + 200 >= 300`
    env, err := cel.NewEnv()
    if err != nil {
        t.Fatal(err)
    }
    ast, iss := env.Compile(str)
    if iss.Err() != nil {
        t.Fatal(iss.Err())
    }
    prog, err := env.Program(ast)
    if err != nil {
        t.Fatal(err)
    }
    out, detail, err := prog.Eval(map[string]interface{}{})
    if err != nil {
        t.Fatal(err)
    }
    fmt.Println(out)
    fmt.Println(detail)
}

輸出結(jié)果:

Running tool: /usr/local/go/bin/go test -timeout 10s -run ^Test_LogicExpr1$ github.com/demo007x/goexpr -v

=== RUN   Test_LogicExpr1
true
<nil>
--- PASS: Test_LogicExpr1 (0.00s)
PASS
ok      github.com/demo007x/goexpr    0.010s

執(zhí)行一個(gè)有函數(shù)的表達(dá)式會(huì)是咋樣的呢?

先定一一個(gè)函數(shù):

type Integer interface {
    ~int | ~int8 | ~int16 | ~int32 | ~int64
}

// 求所有傳入?yún)?shù)的和
func Add[T Integer](param1, param2 T, ints ...T) T {
    sum := param1 + param2
    for _, v := range ints {
        sum += v
    }
    return sum
}

執(zhí)行有函數(shù)的表達(dá)式:6 == Add(age1, age2, age3

func Test_add(t *testing.T) {
    str := `6 == Add(age1, age2, age3)`
    env, _ := cel.NewEnv(
        cel.Variable("age1", cel.IntType),
        cel.Variable("age2", cel.IntType),
        cel.Variable("age3", cel.IntType),
        cel.Function("Add", cel.Overload(
            "Add",
            []*cel.Type{cel.IntType, cel.IntType, cel.IntType},
            cel.IntType,
            cel.FunctionBinding(func(vals ...ref.Val) ref.Val {
                var xx []int64
                for _, v := range vals {
                    xx = append(xx, v.Value().(int64))
                }
                return types.Int(Add[int64](xx[0], xx[1], xx[2:]...))
            }),
        )),
    )
    ast, iss := env.Compile(str)
    if iss.Err() != nil {
        t.Fatal(iss.Err())
    }
    prog, err := env.Program(ast)
    if err != nil {
        t.Fatal(err)
    }
    val, detail, err := prog.Eval(map[string]interface{}{"age1": 1, "age2": 2, "age3": 3})
    if err != nil {
        t.Fatal(err)
    }
    fmt.Print(detail)
    fmt.Println(val)
}

執(zhí)行步驟跟上面的一樣:

  • 首先需要申明傳入函數(shù)參數(shù)的類型 age1, age2 age3
  • 申明 Add 函數(shù),并申明函數(shù)的參數(shù)類型,以及返回結(jié)果
  • env.Compile(str) 字符串的編譯、校驗(yàn)、執(zhí)行,返回 ast
  • prog.Eval 傳入?yún)?shù)執(zhí)行并返回結(jié)果

執(zhí)行結(jié)果:

Running tool: /usr/local/go/bin/go test -timeout 10s -run ^Test_add$ github.com/demo007x/goexpr -v

=== RUN   Test_add
<nil>true
--- PASS: Test_add (0.00s)
PASS
ok      github.com/demo007x/goexpr    0.014s

場(chǎng)景:

一般情況下項(xiàng)目中都很少會(huì)去執(zhí)行一個(gè) CEL 的表達(dá)式,我們都會(huì)按照固定好的邏輯去編寫代碼。

目前低代碼盛行的時(shí)代,項(xiàng)目中的功能都可以自定義,這樣一個(gè)功能就需要足夠的靈活,將一些程序執(zhí)行的邏輯交給用戶去控制。

比如我們目前的項(xiàng)目中:一個(gè)復(fù)雜的流程具體要怎么執(zhí)行,需要誰去審批,需要在那一步的時(shí)候跳過等這些都是可以靈活配置每一個(gè)節(jié)點(diǎn)的邏輯條件,滿足條件的就去執(zhí)行節(jié)點(diǎn)流程,不滿足的就去跳過執(zhí)行下一個(gè)流程處理。這時(shí)候配置條件就可以使用 cel 的表達(dá)式去配置,通過表單中的多個(gè)字段的值組成一個(gè) bool 條件。

比如請(qǐng)假流程:請(qǐng)假天數(shù) <= 3 流程需要走到部門領(lǐng)導(dǎo)審批, 請(qǐng)假天數(shù) > 3 流程需要部門領(lǐng)導(dǎo)審批完成后繼續(xù)流轉(zhuǎn)到部門領(lǐng)導(dǎo)的上級(jí)審批。

這樣一個(gè)條件中請(qǐng)假天數(shù)是一個(gè)表單中某個(gè)字段的值,這樣配置條件就很靈活。這就是 cel 在我們項(xiàng)目中實(shí)際使用的例子的一部分。

cel 在 k8s中的使用

CEL 的每個(gè) Kubernetes API 字段都在 API 文檔中聲明了字段可使用哪些變量。例如,在 CustomResourceDefinitions 的 x-kubernetes-validations[i].rules 字段中,selfoldSelf 變量可用, 并且分別指代要由 CEL 表達(dá)式驗(yàn)證的自定義資源數(shù)據(jù)的前一個(gè)狀態(tài)和當(dāng)前狀態(tài)。 其他 Kubernetes API 字段可能聲明不同的變量。請(qǐng)查閱 API 字段的 API 文檔以了解該字段可使用哪些變量。

K8S 中 CEL 表達(dá)式示例:

規(guī)則用途
self.minReplicas <= self.replicas && self.replicas <= self.maxReplicas驗(yàn)證定義副本的三個(gè)字段被正確排序
'Available' in self.stateCounts驗(yàn)證映射中存在主鍵為 'Available' 的條目
(self.list1.size() == 0) != (self.list2.size() == 0)驗(yàn)證兩個(gè)列表中有一個(gè)非空,但不是兩個(gè)都非空
self.envars.filter(e, e.name = 'MY_ENV').all(e, e.value.matches('^[a-zA-Z]*$')驗(yàn)證 listMap 條目的 'value' 字段,其主鍵字段 'name' 是 'MY_ENV'
has(self.expired) && self.created + self.ttl < self.expired驗(yàn)證 'expired' 日期在 'create' 日期加上 'ttl' 持續(xù)時(shí)間之后
self.health.startsWith('ok')驗(yàn)證 'health' 字符串字段具有前綴 'ok'
self.widgets.exists(w, w.key == 'x' && w.foo < 10)驗(yàn)證具有鍵 'x' 的 listMap 項(xiàng)的 'foo' 屬性小于 10
type(self) == string ? self == '99%' : self == 42驗(yàn)證 int-or-string 字段是否同時(shí)具備 int 和 string 的屬性
self.metadata.name == 'singleton'驗(yàn)證某對(duì)象的名稱與特定的值匹配(使其成為一個(gè)特例)
self.set1.all(e, !(e in self.set2))驗(yàn)證兩個(gè) listSet 不相交
self.names.size() == self.details.size() && self.names.all(n, n in self.details)驗(yàn)證 'details' 映射是由 'names' listSet 中的各項(xiàng)鍵入的

思考

cel 的執(zhí)行流程都是固定的,不管是簡(jiǎn)單的字符串還是內(nèi)嵌函數(shù)的執(zhí)行。那是不是我們就可以基于 go-cel的功能來封裝一次,將相同的邏輯代碼抽取出來。這樣使用的時(shí)候就不需要每執(zhí)行一個(gè) cel 的表達(dá)式就去寫一遍實(shí)現(xiàn)了呢?

我們分析下相同點(diǎn)和不同點(diǎn):

相同點(diǎn):

  • cel.NewEnv 初始化 env
  • env.Compile 檢測(cè),編譯 cel 表達(dá)式
  • env.Program ast 執(zhí)行
  • prog.Eval 執(zhí)行并返回結(jié)果

不同的地方就是需要明確的標(biāo)明變量的類型,以及返回值(函數(shù)),而且參數(shù)個(gè)數(shù)不能多,也不能少,prog.Eval 傳入的實(shí)參只能多不能少,少了就會(huì)報(bào)錯(cuò)。

如果我們將需要傳遞的參數(shù)以及類型提前解析出來并動(dòng)態(tài)的傳入以上幾個(gè)步驟中,那我們的封裝是有意義的。代碼量也減少很多。哪如何抽取動(dòng)態(tài)參數(shù)以及類型呢?

以上就是在golang中使用cel的用法詳解的詳細(xì)內(nèi)容,更多關(guān)于golang中使用cel的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • 詳解go語言是如何實(shí)現(xiàn)協(xié)程的

    詳解go語言是如何實(shí)現(xiàn)協(xié)程的

    go語言的精華就在于協(xié)程的設(shè)計(jì),只有理解協(xié)程的設(shè)計(jì)思想和工作機(jī)制,才能確保我們能夠完全的利用協(xié)程編寫強(qiáng)大的并發(fā)程序,所以本文將給大家介紹了go語言是如何實(shí)現(xiàn)協(xié)程的,文中有詳細(xì)的代碼講解,需要的朋友可以參考下
    2024-04-04
  • Golang自定義結(jié)構(gòu)體轉(zhuǎn)map的操作

    Golang自定義結(jié)構(gòu)體轉(zhuǎn)map的操作

    這篇文章主要介紹了Golang自定義結(jié)構(gòu)體轉(zhuǎn)map的操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧
    2020-12-12
  • 使用Go語言計(jì)算字符串編輯距離的代碼實(shí)現(xiàn)

    使用Go語言計(jì)算字符串編輯距離的代碼實(shí)現(xiàn)

    在自然語言處理、拼寫糾錯(cuò)、模糊搜索等場(chǎng)景中,我們經(jīng)常需要衡量?jī)蓚€(gè)字符串之間的相似度,編輯距離(Edit Distance)  就是一個(gè)經(jīng)典的衡量方式,它描述了將一個(gè)字符串轉(zhuǎn)換為另一個(gè)字符串所需的最少操作次數(shù),本文給大家介紹了如何使用Go語言計(jì)算字符串編輯距離
    2025-07-07
  • Golang使用gob實(shí)現(xiàn)結(jié)構(gòu)體的序列化過程詳解

    Golang使用gob實(shí)現(xiàn)結(jié)構(gòu)體的序列化過程詳解

    Golang struct類型數(shù)據(jù)序列化用于網(wǎng)絡(luò)傳輸數(shù)據(jù)或在磁盤上寫入數(shù)據(jù)。在分布式系統(tǒng)中,一端生成數(shù)據(jù)、然后序列化、壓縮和發(fā)送;在另一端,接收數(shù)據(jù)、然后解壓縮、反序列化和處理數(shù)據(jù),整個(gè)過程必須快速有效
    2023-03-03
  • Golang回調(diào)函數(shù)與閉包和接口函數(shù)的定義及使用介紹

    Golang回調(diào)函數(shù)與閉包和接口函數(shù)的定義及使用介紹

    這篇文章主要介紹了Golang回調(diào)函數(shù)與閉包和接口函數(shù)的定義及使用,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)吧
    2023-05-05
  • Go語言LeetCode500鍵盤行題解示例詳解

    Go語言LeetCode500鍵盤行題解示例詳解

    這篇文章主要為大家介紹了Go語言LeetCode500鍵盤行題解示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-12-12
  • 基于Go語言實(shí)現(xiàn)猜謎游戲

    基于Go語言實(shí)現(xiàn)猜謎游戲

    這篇文章主要為大家詳細(xì)介紹了如何基于Go語言實(shí)現(xiàn)猜謎游戲,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)
    2023-09-09
  • golang常見接口限流算法的實(shí)現(xiàn)

    golang常見接口限流算法的實(shí)現(xiàn)

    本文主要介紹了golang常見接口限流算法的實(shí)現(xiàn),包含固定窗口、滑動(dòng)窗口、漏桶和令牌桶,具有一定的參考價(jià)值,感興趣的可以了解一下
    2025-03-03
  • golang WebDav 多目錄文件服務(wù)的實(shí)現(xiàn)

    golang WebDav 多目錄文件服務(wù)的實(shí)現(xiàn)

    本文介紹使用Golang實(shí)現(xiàn)一個(gè)WebDav服務(wù),支持在一個(gè)端口下為多個(gè)用戶提供獨(dú)立的目錄訪問權(quán)限,通過示例代碼介紹了配置多個(gè)目錄,并實(shí)現(xiàn)基本的用戶認(rèn)證,感興趣的可以了解一下
    2025-12-12
  • Go語言展現(xiàn)快速排序算法全過程的思路及代碼示例

    Go語言展現(xiàn)快速排序算法全過程的思路及代碼示例

    這篇文章主要介紹了Go語言展現(xiàn)快速排序算法全過程的思路及代碼示例,文章最后作者還提到了對(duì)Quick Sort算法優(yōu)化的一些想法,需要的朋友可以參考下
    2016-04-04

最新評(píng)論

邯郸县| 安泽县| 塔城市| 潜江市| 内江市| 夹江县| 青海省| 新田县| 石门县| 鲁甸县| 和平县| 桃江县| 连云港市| 固安县| 台东市| 太原市| 敖汉旗| 濮阳县| 砚山县| 苍溪县| 昆山市| 泗水县| 彭山县| 灵武市| 望江县| 闻喜县| 西盟| 渭南市| 丰县| 同仁县| 湖北省| 漾濞| 基隆市| 当阳市| 台中县| 阳西县| 禄丰县| 定远县| 永川市| 禹城市| 义马市|