181 lines
14 KiB
Markdown
181 lines
14 KiB
Markdown
# Orbit HTTP 与 WebSocket 协议草案
|
||
|
||
> 版本 0.2 · 2026-09-17。HTTP + WS 已确认。本文的具体路径、消息名、字段及状态码是首版设计建议,业务协议尚未实现,需评审后冻结;健康接口的已实现契约见接口手册。
|
||
|
||
实现更新:HTTP 任务/队列接口已完成 P1;Worker 服务端已进入 P2,实际帧名、字段、回执与边界见 [Worker 契约](api/worker.md)。本文保留早期完整设计,未实现项目不能据此视为已交付。
|
||
|
||
## 1. 通用契约
|
||
|
||
- 生产使用 HTTPS / WSS;HTTP 承担任务提交、流程启动与信号、查询、管理,WS 承担 Worker 双向通信与结果订阅。
|
||
- 数据采用 JSON;HTTP 契约使用 OpenAPI 描述,WS 消息使用版本化 JSON Schema 与交互时序描述。
|
||
- ID 与执行代次等可能超出跨语言整数精度的值使用字符串。时间使用 RFC 3339 UTC 字符串,时长字段注明单位。
|
||
- 请求体、消息、批量条数、查询页大小和发送缓冲都必须有限制;具体上限见待定参数表。
|
||
- 鉴权后校验应用、环境、队列和操作权限。请求中的命名空间、Worker ID、任务 ID 不能代替授权。
|
||
- SDK 与服务端维护兼容矩阵;不兼容的协议或处理器版本明确拒绝,不静默解释为当前版本。
|
||
|
||
## 2. HTTP 接口草案
|
||
|
||
| 方法与路径 | 行为 |
|
||
| --- | --- |
|
||
| `POST /v1/tasks` | 幂等提交一项任务 |
|
||
| `POST /v1/tasks:batchSubmit` | 批量逐项提交,每项独立幂等 |
|
||
| `GET /v1/tasks/{task_id}` | 查询权威任务状态与结果摘要 |
|
||
| `GET /v1/tasks/{task_id}/attempts` | 分页查询执行尝试 |
|
||
| `GET /v1/tasks` | 按有索引的字段分页查询,支持提交幂等键精确查找 |
|
||
| `POST /v1/tasks/{task_id}:cancel` | 请求取消,记录原因 |
|
||
| `POST /v1/tasks/{task_id}:retry` | 普通任务人工重试;流程关联任务须经 Weave 恢复入口 |
|
||
| `POST /v1/schedules` | 创建计划,明确漏跑和重叠策略 |
|
||
| `PATCH /v1/schedules/{schedule_id}` | 带预期版本修改计划,冲突时拒绝覆盖 |
|
||
| `POST /v1/schedules/{schedule_id}:pause` | 暂停计划的新触发 |
|
||
| `POST /v1/schedules/{schedule_id}:resume` | 按漏跑策略恢复计划 |
|
||
| `GET /v1/schedules/{schedule_id}/occurrences` | 查询计划触发记录 |
|
||
| `GET /health/live`、`GET /health/ready` | 进程存活与接单就绪状态,二者独立 |
|
||
|
||
应用、凭据、队列、配额和审计管理接口沿用同一鉴权、版本与错误规范,其完整路径清单在接口评审时补齐。就绪检查必须反映关键依赖,数据库不可用时不能继续宣称可安全接单。
|
||
|
||
Weave 的补充接口建议如下,功能范围已确认,具体技术细则以 [流程设计](06-workflow-spec.md) 为准:
|
||
|
||
| 方法与路径 | 行为 |
|
||
| --- | --- |
|
||
| `POST /v1/workflow-definitions/{definition_id}/versions` | 研发通过 SDK 发布不可变定义;同版本同内容幂等,不同内容冲突 |
|
||
| `POST /v1/workflows` | 幂等启动流程,固定定义版本 |
|
||
| `GET /v1/workflows/{workflow_id}` | 查询实例、定义版本、执行状态、暂停标记与等待摘要 |
|
||
| `GET /v1/workflows/{workflow_id}/steps` | 分页查询步骤、输入/结果引用、激活、查询轮次、已用预算与关联任务 |
|
||
| `POST /v1/workflows/{workflow_id}/signals` | 幂等受理信号,不等于信号已经被消费 |
|
||
| `GET /v1/workflows/{workflow_id}/signals/{signal_id}` | 查询信号受理、匹配与消费结果 |
|
||
| `POST /v1/workflows/{workflow_id}:pause` | 持久化暂停门控与审计;不代表在途业务已停止 |
|
||
| `POST /v1/workflows/{workflow_id}:resume` | 解除暂停并安排受控推进;不重置预算、核查状态或原版本 |
|
||
| `POST /v1/workflows/{workflow_id}:cancel` | 记录取消意图,结清关联工作 |
|
||
|
||
流程列表、事件查询与人工核查/恢复接口待细化;恢复必须带预期状态版本、稳定操作身份、原因与证据,不提供任意修改状态的通用入口。
|
||
|
||
暂停和继续使用独立管理权限、预期控制版本、幂等操作身份与原因;同一操作重报返回原确认,过期控制版本不能覆盖后来的操作。建议 `200` 确认门控已提交,后续执行进度通过查询观察;门控失效条件和终态操作错误在协议评审时固定。
|
||
|
||
### 2.1 受理、幂等与错误
|
||
|
||
- 首次创建任务、流程或信号记录的建议响应为 `201 Created`,表示资源及幂等记录已可靠提交,不表示业务执行或信号消费完成。
|
||
- 相同幂等请求返回 `200` 和原资源 ID,标记 `deduplicated: true`;相同键不同语义请求返回 `409`。
|
||
- 单项提交使用 `Idempotency-Key`;批量每项使用 `submission_key`,由 SDK 统一映射为提交幂等标识。
|
||
- 普通任务提交幂等作用域为命名空间内的任务提交操作;流程启动使用独立操作作用域,信号按命名空间、实例与信号 ID 去重。其他管理写操作按命名空间、操作和资源界定作用域,避免同一个键在不同 API 间意外冲突。
|
||
- HTTP `request_id` 仅用于追踪,与业务幂等键分开。
|
||
- 请求超时后,使用原幂等键查询或重试;不能通过换一个键来猜测原请求是否失败。
|
||
- 语义哈希排除请求追踪、连接身份等传输字段,明确 JSON 规范化、缺省值和数组顺序;不能依赖普通 map 的序列化顺序。算法在协议冻结时确定。
|
||
|
||
建议统一响应结构:成功包含 `request_id` 和 `data`;失败包含 `request_id` 和 `error`,其中 `error` 含稳定的 `code`、可读 `message` 和受控的 `details`。
|
||
|
||
| HTTP 状态 | 用途 |
|
||
| --- | --- |
|
||
| 200 / 201 | 查询或已确认的操作结果 / 新资源可靠创建 |
|
||
| 202 | 取消等操作已记录意图,最终行为仍在推进 |
|
||
| 400 / 422 | 协议格式错误 / 语义校验失败 |
|
||
| 401 / 403 | 未认证 / 无操作权限 |
|
||
| 404 | 不存在或按访问策略隐藏的资源 |
|
||
| 409 | 幂等内容冲突、资源版本冲突或操作状态冲突 |
|
||
| 429 / 503 | 容量限制 / 服务暂不可用,可提供 `Retry-After` |
|
||
|
||
批量请求顶层校验通过后可返回 `200`,但每项必须给出索引、任务 ID 或错误。顶层成功不表示每项均被受理。限流或断线后的补交仍使用原逐项幂等键。
|
||
|
||
## 3. Worker 连接
|
||
|
||
建议入口:`GET /v1/worker/connect`,使用 WebSocket Upgrade,协商子协议 `pulse.worker.v1`。SDK 在握手中携带应用凭据,不把长期密钥放在 URL 查询参数中。
|
||
|
||
连接生命周期:鉴权 → 协议协商 → 能力登记 → 恢复核对 → 可接单 → 排空 / 断线。
|
||
|
||
- `worker_instance_id` 标识一个进程实例,重连保留,重启建议新建;可另设可读名称。
|
||
- `session_id` 标识本次连接。新连接需有新的会话身份,旧连接消息不得继续修改新会话的执行权。
|
||
- 恢复执行必须校验认证身份、执行凭据和数据库中的当前归属,不能仅凭相同 Worker 名称认领。
|
||
- Worker 声明队列、处理器兼容版本及容量。平台授权与配额优先于客户端声明。
|
||
|
||
## 4. 消息信封
|
||
|
||
以下为示意,时间与数值不是生产默认参数:
|
||
|
||
```json
|
||
{
|
||
"protocol_version": "1",
|
||
"type": "task.assign",
|
||
"message_id": "msg_example",
|
||
"session_id": "session_example",
|
||
"task_id": "task_example",
|
||
"attempt_id": "attempt_example",
|
||
"body": {
|
||
"handler": "report.generate",
|
||
"handler_version": "1",
|
||
"payload_version": "1",
|
||
"payload": {"report_ref": "report_example"},
|
||
"lease_token": "example-only-not-a-real-secret",
|
||
"lease_expires_at": "2026-09-17T08:00:30Z"
|
||
}
|
||
}
|
||
```
|
||
|
||
响应通过 `reply_to` 关联原消息。需要可靠确认的操作使用稳定消息 ID;跨重连重报时保留操作身份,但使用当前会话认证。传输会话变化不改变操作的语义哈希。
|
||
|
||
执行凭据属于敏感信息,不进入普通日志、管理查询或指标标签。Task / Attempt / Message / Session 四种 ID 分别表达业务工作、执行尝试、通信操作和连接。
|
||
|
||
## 5. 消息类型与确认
|
||
|
||
| 消息 | 方向 | 语义 |
|
||
| --- | --- | --- |
|
||
| `worker.hello` / `worker.welcome` | 双向 | 协商能力、版本、会话与协议限制 |
|
||
| `worker.resume` / `worker.resume.result` | 双向 | 核对断线前的执行尝试,逐项给出续接、停止或核查结果 |
|
||
| `worker.capacity` | Worker → 平台 | 更新容量声明;带会话内序号,重复上报不得重复增加额度 |
|
||
| `task.assign` | 平台 → Worker | 派发已持久化授权的任务与执行凭据 |
|
||
| `task.start` | Worker → 平台 | 确认接单并申请开始;建议获平台确认后再调用处理器 |
|
||
| `task.renew` | Worker → 平台 | 续期有效执行租约,可批量但逐项确认 |
|
||
| `task.complete` | Worker → 平台 | 提交结果或结果引用,等待持久化确认 |
|
||
| `task.fail` | Worker → 平台 | 提交错误分类及已知业务结果,平台按冻结策略决定后续行为 |
|
||
| `task.cancel` | 平台 → Worker | 传递已保存的取消意图,不代表副作用被撤销 |
|
||
| `task.finish` 的 `outcome=CANCELLED` | Worker → 平台 | 已实现的取消确认;需已保存取消意图且本次停止可确认,详见 [取消契约](api/cancellation.md);不使用单独的 task.cancelled 消息 |
|
||
| `worker.drain` | 双向 | 停止接收新任务,受控完成已有任务 |
|
||
| `ack` / `error` | 双向 | 关联明确操作的确认或错误,不能用连接 Pong 替代 |
|
||
|
||
建议采用两步派发:平台先保存 `LEASED` 并发送任务;Worker 发 `task.start`,平台原子确认有效租约和未被取消后保存 `RUNNING`,再确认执行。消息丢失时按同一尝试重报或核对,不能未经重新授权自行执行。
|
||
|
||
如关联执行门控已暂停,尚未获开始授权的 `task.start` 不能被确认可执行;已确认开始的操作重报只补取原确认,不授予第二次执行。具体暂挂、释放预占容量和恢复派发的消息细节在协议冻结时补齐。
|
||
|
||
`RUNNING` 表示平台已授权开始,不足以证明业务侧已经发生某项效果。租约前后派发与取消的竞争见 [可靠性模型](04-reliability-and-data.md)。
|
||
|
||
### 5.1 完成确认丢失
|
||
|
||
1. 平台在同一事务内保存任务终态、尝试结果与待交付事件。
|
||
2. 提交成功后回复 `ack`。
|
||
3. SDK 未收到确认时,重报相同结果或查询任务。
|
||
4. 已确认的同一尝试、同一结果再次上报,返回原确认,即使此时原租约已经到期。
|
||
5. 不同结果冲突或过期尝试试图写入新结果时拒绝修改,保留可诊断记录。
|
||
|
||
先验证权限,再识别已完成操作的幂等重报,最后对新操作校验租约,避免将合法的确认补取误判为任务失败。
|
||
|
||
SDK 可以在进程存活期间缓存未确认消息;不得宣称内存缓存能跨进程崩溃恢复。业务效果已发生但结果尚未回报的情况,依赖业务幂等、业务侧操作记录或结果查询恢复。
|
||
|
||
## 6. 心跳、重连与背压
|
||
|
||
- Ping/Pong 用于连接活性;具体任务的续租必须单独经过授权与持久化确认。[WebSocket 标准](https://www.rfc-editor.org/rfc/rfc6455.html#section-5.5.2)
|
||
- 任务续租失败后,SDK 不能仅因连接仍在线就继续假定执行权有效。
|
||
- SDK 根据服务端确认的租约期限,结合请求耗时、安全余量及本地单调时钟管理停止边界;不直接信任客户端墙上时钟与服务器完全一致。
|
||
- 重连使用有上限的指数退避与抖动;支持凭据刷新、重新鉴权和恢复核对。
|
||
- 断线后允许在最后确认的有效租约内按策略推进;无法及时恢复有效执行权时,停止推进并尽力取消可取消操作。外部已发请求仍需核查。
|
||
- 重连可进入任一平台节点;有效旧尝试经原子校验迁移到新会话,失效尝试不得因重连复活。
|
||
- 平台为已派发未确认的任务预占容量。容量消息采用序号和明确的覆盖/计算规则,重复消息不能释放或重复发放额度。
|
||
- 单连接读写由独立循环管理;设置消息上限、读写期限和有界发送队列,处理器运行不占网络循环。
|
||
- 慢消费者停止追加派发;非关键进度更新可合并,关键状态与结果必须保留恢复路径。
|
||
- 负载均衡器支持 Upgrade、长连接超时和排空;网络断开不能触发无条件的业务重试。
|
||
|
||
## 7. 可靠结果订阅
|
||
|
||
建议独立使用 WSS 入口 `/v1/events/connect` 与子协议 `orbit.events.v1`,区分 Worker 消费权和结果订阅权。具体路径待评审。
|
||
|
||
事件包含 `event_id`、资源类型、资源 ID、状态版本、事件类型及结果引用,可表达任务与流程事件。平台先持久化事件与交付状态,再发送 `event.deliver`;接收方完成自身的可靠处理后发送 `event.ack`。
|
||
|
||
未确认事件可重新发送,订阅方按事件 ID 去重;同一资源按状态版本处理迟到信息,不承诺跨资源全局顺序。事件保留到期、订阅失效与补偿查询行为必须公开。
|
||
|
||
实时控制台展示可使用合并后的进度通知,但业务流程推进不能依赖只有在线时才能收到的消息。
|
||
|
||
Weave 消费 Pulse 的内部持久化事件,不经此对外订阅入口;其事务与去重要求见 [流程协作](06-workflow-spec.md#5-pulse-与-weave-的事务协作)。
|
||
|
||
## 8. 版本与协议验证
|
||
|
||
HTTP `/v1` 与 WS 子协议分别标识兼容边界,业务处理器版本独立。新增可选字段应兼容旧客户端;未知关键消息、状态或枚举不能被当作成功。
|
||
|
||
协议测试应覆盖断线、重复、迟到、会话切换、局部批量失败与凭据撤销。OpenAPI 与 WS Schema 要进入版本控制和兼容性检查,禁止服务端与 SDK 各自维护不同的状态解释。
|