MOSN 变量机制
一、前言
MOSN 为开发者提供了灵活的变量机制,用户通过变量机制能够实现以下目的:
- 获取请求上下文的相关信息
- 在一次请求的生命周期内传递自定义数据 (跨 Filter 传递数据)
- 影响 MOSN 路由框架的运行结果
二、快速开始
假设我们现在正在开发一个 ,该 Filter 处理 Http 请求,且需要实现以下功能:
- 修改
Post
请求 URL 中的Query String
显然,通过请求的 Header
、Body
和 Trailer
是无法获取请求的Method
信息,也无法修改请求的 URL,但我们可以通过变量机制实现以上功能:
从上述简单例子可见,MOSN 变量机制允许开发者灵活地获取请求上下文的相关信息,并按需修改请求的内容。当然,变量机制的功能远不止上述 Filter 展现的能力,下文将详细介绍变量机制的方方面面。
三、现有变量
上文的例子展示了如何通过变量机制获取请求的 Method
信息。除此之外,MOSN 还提供了大量变量,用于提供请求的上下文信息。
下面的表格中,变量名
一列方便开发者在编写代码的过程中获取变量内容;字符串值
一列则方便在配置文件中获取变量值 (例如配置 access_log
时使用 %bytes_received%
获取变量内容)。
需要注意的是:
- 大部分变量通常具有只读属性,若不清楚后续影响,请不要试图修改变量值
- MOSN 的变量机制支持任意类型
interface{}
的变量,但目前提供的绝大多数变量都是 string 类型的,下文如无特殊备注,均为 string 类型。
3.2 Http1 协议变量
3.3 Http2 协议变量
MOSN 还包含一些特殊模块的变量,对于这些变量,基本原则是:如果你不知道要用这些变量,那就不要用
四、增加自定义变量
上文罗列了 MOSN 中已经预定义的所有变量,基本能够满足 MOSN 路由转发场景的需求,若仍有未能满足的定制化需求,可以考虑通过自定义变量的方式来实现。
- 方案一:使用请求 Header 携带自定义数据
- 方案二:使用请求 ctx 携带自定义数据
方案一的缺点在于会污染用户的原始请求,Filter1
塞进 Header 的数据需要在后续 Filter
中清理掉,否则就是被带到上游服务端。此外,受限于 Header 值的类型是 string,方案一无法携带非 string 类型的数据。
方案二的缺点在于性能,ctx 的底层实现是单链表结构,每使用 context.WithValue
添加一个数据时均会将单链表延长一节,且从 ctx 获取数据时,需要遍历单链表,时间复杂度为 O(n)
MOSN 提供的变量机制即是解决上述问题的推荐方案。使用变量机制传递自定义数据具有以下优点:
- 数据类型无限制:除了最基础的 string 类型外,支持 interface{} 类型的自定义数据
- 性能优异:读写自定义变量的时间复杂度均为
- 无侵入性:不侵入用户原始请求、不侵入其它 Filter
MOSN 变量框架提供以下 API 来操作变量: 本节所有 API 均位于 mosn.io/mosn/pkg/variable 包
4.1 注册变量
// 注册新变量
// 需要在 init 函数中调用,否则可能不生效
func Register(variable Variable) error
// 注册前缀变量
// 需要在 init 函数中调用,否则可能不生效
func RegisterPrefix(prefix string, variable Variable) error
// 检查是否已注册某变量
func Check(name string) (Variable, error)
4.2 创建变量
Variable 类型的变量通过以下 API 创建:
对于绝大多数 (99.99%) 使用场景而言,并不需要去理解上述两个 API 的所有入参的含义,MOSN 变量框架提供了默认实现,直接 Ctrl-CV 即可:
// 方式一:
variable.NewVariable("Hello_Variable_1", nil, nil, variable.DefaultSetter, 0)
// 方式二:
// 创建并注册新变量:Hello_Variable_2
variable.NewVariable("Hello_Variable_2", nil, valueGetter, nil, 0)
上述两种方式的主要区别在于新创建的变量是否有初始值:
- 方式一创建的变量没有初始值,需要先调用
Set
方法设置内容后,Get
方法才能获取到 - 方式二创建的变量有初始值,初始值由自定义函数
valueGetter
提供,无需先Set
便可获取到内容
方式一通常更符合自定义变量的使用场景,即自定义变量需要先 后 Get
方式二则是 MOSN 框架自身常用的用法,例如:对于 "Http1_request_method"
这个变量而言,① 它是有初始值的,无需先Set
;② 它的获取方式较为复杂,需要用底层 Http 请求的结构体中获取,因此在 MOSN 中,该变量的创建方式为:
其中 requestMethodGetter
便是从 Http 请求的底层结构体获取 Method 值。
一旦注册好变量,便可通过以下 API 来读写变量:
五、通过变量机制与 MOSN 路由框架进行交互
变量机制除了上文所展现的 “数据搬运工” 的身份外,还具备影响 MOSN 核心路由框架的能力。MOSN 核心路由框架本身会在路由过程中读取某些变量来进行路由决策,因此,开发者可以直接通过变量机制来影响 MOSN 路由的结果。MOSN 变量机制支持以下能力:
5.1 通过变量修改路由元信息
5.2 通过变量指定路由集群
用户可以在 mosn.json 配置中指定用于匹配路由集群的变量名,例如:
{
"routers": [{
"router_config_name": "hello_mosn",
"virtual_hosts": [{
"name": "mosnHost",
"domains": ["*"],
"routers": [{
"match": {"prefix": "/"},
"route": {"cluster_variable": "VAR_NAME"}
}]
}]
}]
在上述配置下,MOSN 在路由时会获取变量 VAR_NAME
的值,并将请求路由到该变量值对应的 Cluster 上:用户可以在路由前 (BeforeRoute
) 的 Filter 中,通过以下代码让请求被路由到 dst_cluster
这个集群上:
用于可以在 mosn.json 配置中指定用于匹配路由的变量名,例如:
在上述配置下,MOSN 在路由时,会将具有变量 VAR_NAME
且变量值为 的请求转发到 dst_cluster
这个集群上
六、总结
综上,变量机制是 MOSN 提供高可扩展性的重要一环,本文对 MOSN 中的变量机制进行了详细介绍,由浅及深,从介绍现有变量开始,到增加自定义变量,再到使用变量与 MOSN 核心路由框架进行交互,供用户一窥变量机制的全貌。