表达式语言

这个页面描述了如何使用 Mixer 的配置表达式语言 (Mixer configuration expression language,或缩写 CEXL)

背景介绍

Mixer 通过一种表达式语言 (CEXL) 去指定 Mixer 遥测策略配置的匹配表达式和映射表达式。这种 CEXL 表达式将一组类型化的属性和常量映射到类型化的

语法

CEXL 表达式支持一部分的 Go 语言表达式,并以之作为 CEXL 的语法。CEXL 表达式自己实现了一部分 Go 语言的操作符,所以它只支持了一部分 Go 语言操作符。在 CEXL 表达式里你可以任意加上括号。

功能

CEXL 表达式支持下列的功能:

运算符/函数定义例子说明
==相等request.size == 200
!=不相等request.auth.principal != "admin"
||逻辑或(request.size == 200) || (request.auth.principal == "admin")
&&逻辑与(request.size == 200) && (request.auth.principal == "admin")
[ ]访问字典request.headers["x-request-id"]
+request.host + request.path
|默认值source.labels["app"] | source.labels["svc"] | "unknown"
match全局匹配match(destination.service, ".ns1.svc.cluster.local")通过指定 字符的位置,匹配以特定字符串作为前缀或后缀的值
email将一个 email 字符串转换为一个 EMAIL_ADDRESS 类型email("awesome@istio.io")使用 email 函数创建一个 EMAIL_ADDRESS 类型的字面量
dnsName将一个域名字符串转换为一个 DNS_NAME 类型dnsName("www.istio.io")使用 dnsName 函数创建一个 DNS_NAME 类型的字面量
ip将一个 IPv4 地址字符串转换为一个 IP_ADDRESS 类型source.ip == ip("10.11.12.13")使用 ip 函数创建一个 IP_ADDRESS 类型的字面量
timestamp将一个 RFC 3339 格式的时间字符串转换为一个 TIMESTAMP 类型timestamp("2015-01-02T15:04:35Z")使用 timestamp 函数创建一个 TIMESTAMP类型的字面量
uri将一个 URI 字符串转换为一个 URI 类型uri("http://istio.io")使用 uri 函数创建一个 URI 类型的字面量
.matches正则表达式匹配"svc.".matches(destination.service)用正则表达式 "svc." 匹配 destination.service
.startsWith匹配字符串前缀destination.service.startsWith("acme")匹配 destination.service 字符串是否以 "acme" 开始
.endsWith匹配字符串后缀destination.service.endsWith("acme")匹配 destination.service 字符串是否以 "acme" 结束
emptyStringMap创建一个空字符串字典request.headers | emptyStringMap()emptyStringMap 函数创建一个空字符串字典作为 request.headers 的默认值
conditional模拟三元操作符conditional((context.reporter.kind | "inbound") == "outbound", "client", "server")如果 reporter.kind 的值是 "outbound" 的话,返回 "client",否则返回 "server"
toLower将字符串转换成小写toLower("User-Agent")返回 "user-agent"

类型检查

CEXL 表达式里的变量来自属性表里定义的属性,常量是隐式类型的,而函数是显式类型化的。

Mixer 在校验配置信息时,会校验其中的 CEXL 表达式,并将它转换为对应的类型。选择表达式必须解析成布尔值,而映射表达式必须解析为它们映射到的类型。如果选择表达式无法解析成布尔值,或者映射表达式解析成错误的类型,配置校验就会失败。

例如,如果一个需要传 string 类型的操作,但是传了 request.size | 200 表达式,配置校验就会失败,因为表达式经过解析后是一个整型值。

默认值

如果表达式尝试读取一个不存在的属性,表达式解析会失败。使用 | 操作符可以为这次读取属性设置一个默认值。

举个例子,如果 request.auth.principal 值不存在,在解析 request.auth.principal == "user1" 这个表达式时就会失败。可以用 | (OR) 操作符解决这个问题,比如将表达式改为: (request.auth.principal | "nobody" ) == "user1"

例子

表达式返回类型说明
request.size | 300int如果 request.size 存在,则返回,否则表达式值为整型 200
request.headers["x-forwarded-host"] == "myhost"boolean
(request.headers["x-user-group"] == "admin") || (request.auth.principal == "admin")boolean如果用户为 admin,或者用户属于 admin 组,表达式为 true
(request.auth.principal | "nobody" ) == "user1"boolean如果 request.auth.principal 的值的是 “user1”,表达式值为 true,表达式解析不会因为 request.auth.principal 不存在而失败
source.labels["app"]=="reviews" && source.labels["version"]=="v3"boolean如果 app label 的值为 “reviews” 而且 version label 是 “v3”,表达式值为 true,否则为 false