wasm 和 grpc 服务通过一份共享契约与内核通信。作为服务开发者,您会使用为所选运行时生成的 SDK,而不是自己构建这套线上格式;本页描述这份契约的整体形态,让您了解自己的服务能期待什么、又需要负责什么。static 服务完全不使用这套协议——参阅 Static 服务。
两个通信方向
启动阶段:身份握手
当内核启动一个wasm 或 grpc 服务时,该服务会执行一次简短的身份握手:确认自己的名称和版本,并接收内核需要交给它的一切,例如已配置的参数。这次握手本身并不会声明该服务提供了什么。
运行期间:声明传输和路由
握手之后,服务通过契约中内核这一侧的接口,向内核注册传输和路由,来告知内核自己暴露了什么;而且只要服务保持运行,就可以在任意时刻再次添加或移除它们——不仅仅是在启动时。关于传输和路由各自包含什么、又是如何关联的,请参阅路由与传输。注册失败的传输或路由不会导致服务停止运行;它会将该服务标记为degraded,而其他资源仍会正常工作。
优雅关闭是对称的:服务应当先移除路由,再移除它们所依赖的传输;当一个服务会话结束时,内核会强制清理遗留的一切,因此一个已停止或崩溃的服务不会留下仍可访问的失效路由。
处理请求和事件
一旦路由注册成功,匹配的 HTTP 请求就会被转换为带类型的消息,并投递给您服务的处理程序。一个 HTTP 请求携带方法、路径、查询字符串、请求头、请求体,以及已认证的用户(如果有)。您的处理程序返回状态码、响应头和响应体。请求体和响应体是作为完整消息交换的,而不是流——请将负载控制在内核规定的大小限制之内,具体说明见 HTTP 传输页面。选择运行时
wasm 和 grpc 服务使用同一套契约,区别只在于它跨越进程边界的方式不同。
gRPC
您可以用任何支持 Protobuf 和 gRPC 的语言实现grpc 服务。该服务作为独立进程运行,并通过 gRPC 与内核通信。使用您所用语言的 Protobuf 和 gRPC 工具生成或实现契约类型,然后在服务清单中设置 Type: grpc。
WASM
Arupa 目前仅支持使用 Go 编写 WASM 服务。使用为 Arupa 契约生成的 Go SDK,并将服务编译为 WASM。该 WASM 集成基于knqyf263/go-plugin,它会生成 Go 接口,并将底层的 WASM 通信细节隐藏在 SDK 之后。
请遵循该指南中描述的开发模式:定义或使用契约、生成 Go 绑定代码、实现生成的接口,并将实现编译为 WASM 模块。在服务清单中设置 Type: wasm。
传输方式会变化,但消息类型及其含义保持不变。请根据清单中的 Type 选择匹配的 SDK 和构建流程。
使用生成的 SDK
请将生成的文件视为构建产物,不要手动编辑它们。如果您需要用到新增的消息或字段,请从对应的 schema 重新生成 SDK,并重新构建您的服务。 在info.yaml 中将 ContractVersion 设置为您的服务所使用的协议版本。该版本必须被将要加载该包的内核所支持。清单中的版本号和生成的 SDK 应当始终一起更新。
使用生成的 SDK 可以为您的服务带来:
- 带类型的请求和响应值;
- 与内核一致的序列化方式;
- 由 SDK 处理的、特定于运行时的通信细节;以及
- 无论
Type为何,都统一的编程模型。
保持服务的兼容性
在使用更新的契约时,请记住字段是按编号而不仅仅按名称来标识的:- 保持已有字段编号所对应的原始含义不变;
- 不要重用已删除字段的编号;
- 使用新的编号添加新字段;以及
- 当变更是向后兼容的时候,允许您的服务忽略它不需要的字段。
典型开发流程
- 选择
wasm或grpc,并使用对应生成的 SDK。 - 使用 SDK 提供的类型实现契约和您的功能逻辑。
- 注册服务提供的传输和路由,并在服务运行期间,如果所暴露的内容发生变化,随时保持它们的最新状态。
- 每当您的功能需要一项内核所拥有的能力时,使用 Host 绑定。
- 在服务清单中设置匹配的
ContractVersion。 - 构建并打包服务,然后使用支持该契约版本的内核加载它。