MOSN 变量机制

一、前言

MOSN 为开发者提供了灵活的变量机制,用户通过变量机制能够实现以下目的:

  • 获取请求上下文的相关信息
  • 在一次请求的生命周期内传递自定义数据 (跨 Filter 传递数据)
  • 影响 MOSN 路由框架的运行结果

二、快速开始

假设我们现在正在开发一个 ,该 Filter 处理 Http 请求,且需要实现以下功能:

  • 修改 Post请求 URL 中的 Query String

显然,通过请求的 HeaderBodyTrailer是无法获取请求的Method信息,也无法修改请求的 URL,但我们可以通过变量机制实现以上功能:

从上述简单例子可见,MOSN 变量机制允许开发者灵活地获取请求上下文的相关信息,并按需修改请求的内容。当然,变量机制的功能远不止上述 Filter 展现的能力,下文将详细介绍变量机制的方方面面。

三、现有变量

上文的例子展示了如何通过变量机制获取请求的 Method信息。除此之外,MOSN 还提供了大量变量,用于提供请求的上下文信息。

下面的表格中,变量名一列方便开发者在编写代码的过程中获取变量内容;字符串值一列则方便在配置文件中获取变量值 (例如配置 access_log时使用 %bytes_received% 获取变量内容)。

需要注意的是:

  1. 大部分变量通常具有只读属性,若不清楚后续影响,请不要试图修改变量值
  2. 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 注册变量

  1. // 注册新变量
  2. // 需要在 init 函数中调用,否则可能不生效
  3. func Register(variable Variable) error
  4. // 注册前缀变量
  5. // 需要在 init 函数中调用,否则可能不生效
  6. func RegisterPrefix(prefix string, variable Variable) error
  7. // 检查是否已注册某变量
  8. func Check(name string) (Variable, error)

4.2 创建变量

Variable 类型的变量通过以下 API 创建:

对于绝大多数 (99.99%) 使用场景而言,并不需要去理解上述两个 API 的所有入参的含义,MOSN 变量框架提供了默认实现,直接 Ctrl-CV 即可:

  1. // 方式一:
  2. variable.NewVariable("Hello_Variable_1", nil, nil, variable.DefaultSetter, 0)
  3. // 方式二:
  4. // 创建并注册新变量:Hello_Variable_2
  5. 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 配置中指定用于匹配路由集群的变量名,例如:

  1. {
  2. "routers": [{
  3. "router_config_name": "hello_mosn",
  4. "virtual_hosts": [{
  5. "name": "mosnHost",
  6. "domains": ["*"],
  7. "routers": [{
  8. "match": {"prefix": "/"},
  9. "route": {"cluster_variable": "VAR_NAME"}
  10. }]
  11. }]
  12. }]

在上述配置下,MOSN 在路由时会获取变量 VAR_NAME的值,并将请求路由到该变量值对应的 Cluster 上:用户可以在路由前 (BeforeRoute) 的 Filter 中,通过以下代码让请求被路由到 dst_cluster这个集群上:

用于可以在 mosn.json 配置中指定用于匹配路由的变量名,例如:

在上述配置下,MOSN 在路由时,会将具有变量 VAR_NAME且变量值为 的请求转发到 dst_cluster这个集群上

六、总结

综上,变量机制是 MOSN 提供高可扩展性的重要一环,本文对 MOSN 中的变量机制进行了详细介绍,由浅及深,从介绍现有变量开始,到增加自定义变量,再到使用变量与 MOSN 核心路由框架进行交互,供用户一窥变量机制的全貌。