目前 Mesh 本身就有非常多的协议支持的诉求,原来的方式是直接用 Golang 写协议解析的代码,现在通过 Wasm 可以让 MOSN 更好地以更加灵活以及可扩展的方式去支持协议开发。本文主要介绍基于 Wasm 实现开放协议扩展流程和原理,更好的帮助开发者理解和更容易接入 Mesh。
背景
在云原生趋势下,对厂商和客户扩展语言应该是包容的,不应该将用户的技术栈绑定到 Sidecar 语言中,我们关注到 Wasm 能解决这个问题,它提供沙箱隔离机制,允许多语言编写代码打包成 Wasm文件,然后嵌入到 Sidecar 中执行。
理想情况下,MOSN 保证核心组件稳定,厂商私有代码不应该合并到 MOSN 中,应该具备按需扩展能力即可,MOSN 具备灵活的扩展能力,以及对多语言友好、稳定和安全性高等能力。本文重点会以 Go 语言为例,讲解如何基于 Wasm 对 MOSN 的协议进行扩展。
协议拓展
在具体讲解扩展前,简单介绍一下使用 Wasm 扩展的优缺点。
-
隔离性 : Wasm 扩展将运行在资源受限的沙箱中,扩展代码的漏洞及崩溃都无法传导到沙箱之外,沙箱所使用的 CPU、内存资源等受宿主机(MOSN)控制。 -
安全性 : Wasm 扩展只能通过一组有限的、明确定义的 ABI 与 MOSN 进行通信,MOSN 对该 ABI 具有完全的控制权,这使得 Wasm 扩展只能使用 MOSN 允许的能力、访问受许可的资源。 -
敏捷性 : Wasm 扩展框架允许在不重启 MOSN 的前提下,动态加载、更新、卸载 Wasm 扩展插件。 -
灵活性 : 可以使用多种语言编写 Wasm 扩展,例如: Go、C++、Rust 等,甚至直接复用社区扩展插件。
-
增加性能开销:目前沙箱和宿主机内存隔离,插件和 MOSN 数据交换需要通过 ABI 和内存 Copy,会增加性能开销。 成熟度相对不足:目前 Wasm runtime 还需要进行生产验证,目前基于 C 的 Wasm 实现相对比较成熟。
1. 模块装载流程
2. 请求/响应流程
-
请求/响应到达 NETWORK/IO 层。 -
通过协议去解码 Buffer 数据流,创建上下文。 -
生成 Stream,封装帧以及连接信息。 -
经过 Proxy 层进行路由转发, 编码请求/响应。
3. 编解码流程
-
数据报文委托给扩展协议 Wasm Protocol 解码。 -
沙箱内扩展协议解码被调用,返回 Command。 -
Command 在转发前,委托给 Wasm Protocol 编码。 -
沙箱内开发者扩展编码被调用,返回 Buffer。
-
当 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 发起调用:
Qiuck Start
本小节主要演示快速跑通协议扩展流程,我们基于 Wasm 扩展机制实现 wasm-bolt 协议插件(基于原生 bolt 协议),跑通主流程比较简单,分为以下步骤:
提供插件代码,并打包成 bolt-go.wasm 文件。
启动 MOSN 并装载 bolt-go.wasm 插件。
启动 JAVA SOFABoot服务端和客户端程序。
1. 编写协议扩展
如果直接在本地编译,需要 tiny-go >= 0.17.0 版本, 可以在 examples 目录 bolt 中执行命令:
2. 启动 MOSN
如果是研发同学,可以根据 Step 2 拉取代码,直接通过 intellij idea 右键项目根目录 Debug(这样就不用手动去编译且不需要命令行启动 MOSN 了),在 Edit Configurations... 调试配置页签中修改包路径和程序入口参数:
3. 启动 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 请求):
https://github.com/mosn/mosn/pull/1597?spm=ata.21736010.0.0.4e6513eeCnOrtd
- mosn api #31:
- 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.json
example 目录:
https://github.com/zonghaishang/proxy-wasm-sdk-go/tree/master/examples/bolt
延伸阅读
本文分享自微信公众号 - 金融级分布式架构(Antfin_SOFA)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。