Protocol Extension Base On Wasm——协议扩展篇

百家 作者:InfoQ 2021-04-22 20:28:56
目前 Mesh 本身就有非常多的协议支持的诉求,原来的方式是直接用 Golang 写协议解析的代码,现在通过 Wasm 可以让 MOSN 更好地以更加灵活以及可扩展的方式去支持协议开发。本文主要介绍基于 Wasm 实现开放协议扩展流程和原理,更好的帮助开发者理解和更容易接入 Mesh。
背 ? ? 景

在云原生趋势下,对厂商和客户扩展语言应该是包容的,不应该将用户的技术栈绑定到 Sidecar 语言中,我们关注到 Wasm 能解决这个问题,它提供沙箱隔离机制,允许多语言编写代码打包成 Wasm 文件,然后嵌入到 Sidecar 中执行。理想情况下,MOSN 保证核心组件稳定,厂商私有代码不应该合并到 MOSN 中,应该具备按需扩展能力即可,MOSN 具备灵活的扩展能力,以及对多语言友好、稳定和安全性高等能力。本文重点会以 Go 语言为例,讲解如何基于 Wasm 对 MOSN 的协议进行扩展。

协议拓展

在具体讲解扩展前,简单介绍一下使用 Wasm 扩展的优缺点。

使用 Wasm 扩展将带来以下好处:

  • 隔离性 : Wasm 扩展将运行在资源受限的沙箱中,扩展代码的漏洞及崩溃都无法传导到沙箱之外,沙箱所使用的 CPU、内存资源等受宿主机(MOSN)控制。

  • 安全性 : Wasm 扩展只能通过一组有限的、明确定义的 ABI 与 MOSN 进行通信,MOSN 对该 ABI 具有完全的控制权,这使得 Wasm 扩展只能使用 MOSN 允许的能力、访问受许可的资源。

  • 敏捷性 : Wasm 扩展框架允许在不重启 MOSN 的前提下,动态加载、更新、卸载 Wasm 扩展插件。

  • 灵活性 : 可以使用多种语言编写 Wasm 扩展,例如: Go、C++、Rust 等,甚至直接复用社区扩展插件。

使用 Wasm 扩展包含以下缺点:

  • 增加性能开销:目前沙箱和宿主机内存隔离,插件和 MOSN 数据交换需要通过 ABI 和内存 Copy,会增加性能开销。

  • 成熟度相对不足:目前 Wasm runtime 还需要进行生产验证,目前基于 C 的 Wasm 实现相对比较成熟。

  1. 模块装载流程在讲解模块装载流程前,我们先看一段最小化激活 MOSN Wasm 扩展的配置:



可以看到,激活扩展插件的配置相对来说比较清晰明了。主要提供插件路径和指定引擎(标记 1、2 和 3),在 MOSN 扩展点位置引用插件 ID 即可(标记 4、5 和 6)。接下来,我们看下装载流程:


开发同学编写好协议插件后,然后编译为.wasm 文件,在 MOSN 主进程启动期间,根据提供的配置文件(插件路径、filter 中正确指定插件 ID),MOSN 会根据配置的引擎名称、配置文件装载 .wasm 文件,初始化 Wasm Module,然后创建对应的沙箱实例。以协议举例,当有请求进来时,会触发插件的解码逻辑,插件解码完成后,会主动告知 MOSN 解码完成,由 MOSN 生成解码后的请求对象(Frame)。

  1. 请求 / 响应流程

在 MOSN 分层中包括四层,IO、Protocol、Stream 和 Proxy 层,IO 层主要用来读取网络层数据。Protocol 层提供协议编解码能力。Stream 层用于封装请求 / 响应帧,也会保存一些链接(downstream&upsteam)信息。Proxy 层用来做路由寻址转发等能力。在 RPC 场景中,请求和响应流程都会经过以下步骤:

  • 请求 / 响应到达 NETWORK/IO 层。

  • 通过协议去解码 Buffer 数据流,创建上下文。

  • 生成 Stream,封装帧以及连接信息。

  • 经过 Proxy 层进行路由转发, 编码请求 / 响应。

在支持基于 Wasm 虚拟机扩展协议时,会在 Protocol 层扩展一个被称为 Wasm Protocol 代理实现,在协议模块装载时,会创建 Wasm Protocol 实例去代理开发者协议插件。因为在 MOSN 中 Protocol 是单实例的,需要在 MOSN 中同时支持多个协议扩展,所以需要为开发者协议插件实现创建非共享的 Wasm Protocol 实例。

  1. 编解码流程一次标准的请求响应,需要考虑很多情况,不仅仅是编解码,还有心跳和 Hijacker 等过程,为了通俗易懂,本文主要以编解码为场景,讲解使用 Wasm 进行协议扩展。

一次标准请求到达 MOSN 时,在整个转发周期中,需要先经过 Decode(协议解码),然后再通过 Encode(协议编码)生成二进制流,通过 Socket 转发出去。基于 Wasm 协议扩展,会经过以下步骤:

  • 数据报文委托给扩展协议 Wasm Protocol 解码。

  • 沙箱内扩展协议解码被调用,返回 Command。

  • Command 在转发前,委托给 Wasm Protocol 编码。

  • 沙箱内开发者扩展编码被调用,返回 Buffer。


Question:

  1. MOSN 中 Protocol 是单实例的,基于 Wasm 是如何支持多个协议同时扩展的?在 Wasm Module 装载时,解析 filter_chains 时会给扩展协议注册一份 Wasm Protocol 扩展实例, 这样 Wasm Protcol 就能准确识别代理具体协议了。沙箱内存隔离,如何解决协议数据类型透传? 因为不像动态链接库那样,同一个类型加载插件和 Host 共享类型。Host 的 Command 传给 Plugin,Plugin 的 Command 是开发者私有对象,需要类型匹配。在协议扩展中,沙箱内插件请求 Command 类型不能和 Host 共享,因此 Command 类型无法传递。因为插件和 Host 之间通信采用 ABI 交互,因此我们在 ABI 接口设计上采用通用协议传输,以解码 ABI(插件上报给 Host)格式举例:


接下来,我们更进一步拆解编解码内部流程 Host 是如何与沙箱插件进行交互的:

  • 当 IO 数据流到达时,Connection 会分发(dispatch)Buffer, 会创建 downstream 的上下文 Context。

  • 在调用 Wasm Protocol 的解码之前,会调用沙箱插件 OnContextCreate 方法创建插件上下文(简称 Wasm Context), Wasm Context 对象会保存在 Host 的上下文中,用于回调插件生命周期方法。

  • Host 调用插件解码方法,会通过 abi 规范方法传递 Buffer 字节和长度,同时也会把当前会话的 contextID 透传给插件。

  • 沙箱内 SDK 会根据 contextID 查找已经创建的 Protocol Context (开发者提供的协议插件),调用协议解码并生成 Command。

  • 在整个 Decode 的流程中,Host 和沙箱插件已经获得锁。沙箱插件会根据解出的 Command,生成 Host 侧能理解的 Command 结构,由 Wasm Protocol 生成 Request 或者 Response 类型 Command。

  • 在 Host 转发请求到远端主机时,会再次调用 Wasm Protocol 进行一次编码, 这里会通过编码 ABI 接口,同时会把 contextID 透传给插件。

  • 沙箱内 SDK 会根据 contextID 查找已经创建的 ProtocolContext (开发者提供的协议插件),会先用第 4 步生成的 Command 作为参数,传递给协议插件编码(encode)入参, 如果此时 Host 侧 Header 和 Content 有变更,在传递给协议插件之前,会更新 Command 的 Header 和 Content,保证 Host 侧的内容不会丢失。

  • 当协议插件编码生成 Buffer 时,沙箱 SDK 会负责将编码数据 Copy 到 Host 侧(通过 ABI 接口),然后通过 Connection 发送出去。

  • 当收到响应时,针对 Response 的 Command,会创建新的 Context,步骤 1~8 会重新执行一遍。特殊的情况,在收到响应时,Host 清理资源时,会将请求的 Wasm Context 和响应的 Wasm Context 一并清除, 防止内存泄露。

沙箱 SDK 将编码数据 Copy 到 Host 侧,通过以下 ABI 接口对 Host 发起调用:


为了澄清 Context 之间的关系,在这里做一些简要说明。在下图中的 Context 我们是指沙箱内插件的上下文 ( Wasm Context )。在 Wasm 文件被加载进 Host 时,Host 会解析并创建对应的 Module,基于 Module 创建等价于 CPU 数量的沙箱实例。Root Context 一般用于全局的生命周期回调,比如虚拟机启动通知、插件激活通知和定时器等。Host 侧会为每个会话 (请求级别) 创建对应的 Context (这里对应于沙箱内部上下文),沙箱实例能够处理多笔请求。Host 本身实现会创建 Context,这个和沙箱的上下文是独立的,没有 Wasm 扩展之前它会存一些连接 ID、当前 listener 类型和协议信息等,有了 Wasm 之后,我们会在 Host 的 Context 中存储 Wasm Context 信息而已。

Qiuck Start

本小节主要演示快速跑通协议扩展流程,我们基于 Wasm 扩展机制实现 wasm-bolt 协议插件(基于原生 bolt 协议),跑通主流程比较简单,分为以下步骤:

  • 提供插件代码,并打包成 bolt-go.wasm 文件。

  • 启动 MOSN 并装载 bolt-go.wasm 插件。

  • 启动 JAVA SOFABoot 服务端和客户端程序。

演示场景:MOSN 端会监听端口 2045 和 2046,当 2045 端口监听到 SOFABoot 客户端发来的请求时,会转发给 2046 端口,最终会把请求转发给 SOFABoot 服务端应用程序。



  1. 编写协议扩展基于 wasm-bolt 插件示例我们已经编译好了,可以作为演示程序直接使用。如果希望自己手动编译, 提供 2 种方式进行编译。采用镜像的方式开发环境编译,在 examples 目录 bolt (路径附在文章末端)中执行命令,编译成功后会在 build 目录生成 bolt-go.wasm 文件:

如果直接在本地编译,需要 tiny-go >= 0.17.0 版本, 可以在 examples 目录 bolt 中执行命令:

  1. 启动 MOSN目前提供了一份用于 Wasm 启动的配置文件(路径附在文章末端) mosn_rpc_config_wasm.json,可以使用以下命令启动 MOSN:

提示:mosn_rpc_config_wasm 中已经配置了 bolt-go.wasm,在项目根目录 etc/wasm/ 目录中。

其中, mosnd 可执行文件可以通过编译 MOSN 获取, 执行以下命令:

如果是研发同学,可以根据 Step 2 拉取代码,直接通过 intellij idea 右键项目根目录 Debug(这样就不用手动去编译且不需要命令行启动 MOSN 了),在 Edit Configurations... 调试配置页签中修改包路径和程序入口参数:

  1. 启动 SOFABoot目前 SOFABoot 应用测试程序已经托管到 Github 上,可以通过以下命令获取:

启动 SOFABoot 服务端程序:

java -DMOSN_ENABLE=true -Drpc_tr_port=12199 -Dspring.profiles.active=dev -Drpc_register_registry_ignore=true -jar sofa-echo-server-web-1.0-SNAPSHOT-executable.jar

然后启动 SOFABoot 客户端程序:

java -DMOSN_ENABLE=true -Drpc_tr_port=12198 -Dspring.profiles.active=dev -Drpc_register_registry_ignore=true -jar sofa-echo-client-web-1.0-SNAPSHOT-executable.jar

当客户端启动成功后,会在终端输出以下信息(每隔 1 秒发起一次 Wasm 请求):

当前扩展特性已经合并进开源社区:
wasm protocol #1579:
https://github.com/mosn/mosn/pull/1597?spm=ata.21736010.0.0.4e6513eeCnOrtd
mosn api #31:
https://github.com/mosn/api/pull/31?spm=ata.21736010.0.0.4e6513eeCnOrtd
wasm sdk-go:
https://github.com/zonghaishang/proxy-wasm-sdk-go?spm=ata.21736010.0.0.4e6513eeCnOrtd
附:
Wasm 启动配置文件:
https://github.com/mosn/mosn/blob/master/configs/mosn_rpc_config_wasm.json?spm=ata.21736010.0.0.4e6513eeQRr96Y&file=mosn_rpc_config_wasm.jsonexample?
目录:
https://github.com/zonghaishang/proxy-wasm-sdk-go/tree/master/examples/bolt

?活动推荐

架构的下一个十年是什么样的?云的下一站是什么?移动开发还有没有未来?7 月 9-10 日,走进 ArchSummit 深圳 2021,收获 2 天技术干货,与近百位大咖面对面交流,你不仅能从他们那得到一些答案,还能 get 他们的成长经验。目前深圳站 7 折优惠折扣即将结束,有购票需求联系票务小姐姐 18514549229(同微信)。

点击底部【阅读原文】,随时掌握架构师峰会深圳站最新动态。

点个在看少个 bug

关注公众号:拾黑(shiheibook)了解更多

[广告]赞助链接:

四季很好,只要有你,文娱排行榜:https://www.yaopaiming.com/
让资讯触达的更精准有趣:https://www.0xu.cn/

公众号 关注网络尖刀微信公众号
随时掌握互联网精彩
赞助链接