> ## Documentation Index
> Fetch the complete documentation index at: https://docs.arupa.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# 协议

> 了解内核与服务之间是如何通信的。

`wasm` 和 `grpc` 服务通过一份共享契约与内核通信。作为服务开发者，您会使用为所选运行时生成的 SDK，而不是自己构建这套线上格式；本页描述这份契约的整体形态，让您了解自己的服务能期待什么、又需要负责什么。`static` 服务完全不使用这套协议——参阅 [Static 服务](./static-service)。

## 两个通信方向

```text theme={null}
内核 ── 请求与事件 ──────────────▶ 您的服务
内核 ◀─ 共享能力调用 ──────────── 您的服务
```

传入的 HTTP 请求会以带类型的消息形式交给您的服务，您的处理程序也以带类型的消息返回结果。另一方面，每当您的服务需要内核所拥有的某项能力时——键值存储、日志记录、调用另一个服务——它会通过同一套生成的 SDK 回调内核。请将功能特有的状态和行为保留在您的服务中；只把内核这一侧的契约用于内核确实拥有的能力。

## 启动阶段：身份握手

当内核启动一个 `wasm` 或 `grpc` 服务时，该服务会执行一次简短的身份握手：确认自己的名称和版本，并接收内核需要交给它的一切，例如已配置的参数。这次握手本身并不会声明该服务提供了什么。

## 运行期间：声明传输和路由

握手之后，服务通过契约中内核这一侧的接口，向内核注册传输和路由，来告知内核自己暴露了什么；而且只要服务保持运行，就可以在任意时刻再次添加或移除它们——不仅仅是在启动时。关于传输和路由各自包含什么、又是如何关联的，请参阅[路由与传输](./routing)。注册失败的传输或路由不会导致服务停止运行；它会将该服务标记为 `degraded`，而其他资源仍会正常工作。

优雅关闭是对称的：服务应当先移除路由，再移除它们所依赖的传输；当一个服务会话结束时，内核会强制清理遗留的一切，因此一个已停止或崩溃的服务不会留下仍可访问的失效路由。

## 处理请求和事件

一旦路由注册成功，匹配的 HTTP 请求就会被转换为带类型的消息，并投递给您服务的处理程序。一个 HTTP 请求携带方法、路径、查询字符串、请求头、请求体，以及已认证的用户（如果有）。您的处理程序返回状态码、响应头和响应体。请求体和响应体是作为完整消息交换的，而不是流——请将负载控制在内核规定的大小限制之内，具体说明见 [HTTP 传输](./transport-http)页面。

## 选择运行时

`wasm` 和 `grpc` 服务使用同一套契约，区别只在于它跨越进程边界的方式不同。

### gRPC

您可以用任何支持 Protobuf 和 gRPC 的语言实现 `grpc` 服务。该服务作为独立进程运行，并通过 gRPC 与内核通信。使用您所用语言的 Protobuf 和 gRPC 工具生成或实现契约类型，然后在服务清单中设置 `Type: grpc`。

### WASM

Arupa 目前仅支持使用 Go 编写 WASM 服务。使用为 Arupa 契约生成的 Go SDK，并将服务编译为 WASM。该 WASM 集成基于 [`knqyf263/go-plugin`](https://github.com/knqyf263/go-plugin)，它会生成 Go 接口，并将底层的 WASM 通信细节隐藏在 SDK 之后。

请遵循[该指南](https://github.com/knqyf263/go-plugin)中描述的开发模式：定义或使用契约、生成 Go 绑定代码、实现生成的接口，并将实现编译为 WASM 模块。在服务清单中设置 `Type: wasm`。

传输方式会变化，但消息类型及其含义保持不变。请根据清单中的 `Type` 选择匹配的 SDK 和构建流程。

## 使用生成的 SDK

请将生成的文件视为构建产物，不要手动编辑它们。如果您需要用到新增的消息或字段，请从对应的 schema 重新生成 SDK，并重新构建您的服务。

在 `info.yaml` 中将 `ContractVersion` 设置为您的服务所使用的协议版本。该版本必须被将要加载该包的内核所支持。清单中的版本号和生成的 SDK 应当始终一起更新。

使用生成的 SDK 可以为您的服务带来：

* 带类型的请求和响应值；
* 与内核一致的序列化方式；
* 由 SDK 处理的、特定于运行时的通信细节；以及
* 无论 `Type` 为何，都统一的编程模型。

## 保持服务的兼容性

在使用更新的契约时，请记住字段是按编号而不仅仅按名称来标识的：

* 保持已有字段编号所对应的原始含义不变；
* 不要重用已删除字段的编号；
* 使用新的编号添加新字段；以及
* 当变更是向后兼容的时候，允许您的服务忽略它不需要的字段。

不要在您服务的本地假设中改变某个已有字段的含义或类型。如果某项变更不是向后兼容的，请使用对应的新契约版本，并针对该版本重新构建服务。

## 典型开发流程

1. 选择 `wasm` 或 `grpc`，并使用对应生成的 SDK。
2. 使用 SDK 提供的类型实现契约和您的功能逻辑。
3. 注册服务提供的传输和路由，并在服务运行期间，如果所暴露的内容发生变化，随时保持它们的最新状态。
4. 每当您的功能需要一项内核所拥有的能力时，使用 Host 绑定。
5. 在服务清单中设置匹配的 `ContractVersion`。
6. 构建并打包服务，然后使用支持该契约版本的内核加载它。

这里的重要边界很简单：您的服务负责实现行为，而协议和生成的 SDK 负责在您的代码和内核之间传递带类型的数据。
