跳到正文

ELR2 协议

ELR2 是 Elura 用于客户端流量和内部命令流量的二进制帧协议。所有整数字段均按 网络字节序(大端)编码。

帧布局

每个帧都以固定的 28 字节帧头开始:

偏移大小字段说明
04Magic0x454C5232,ASCII ELR2
42Version2
61Kind请求 1、响应 2、Push 3、错误 4
71Flags当前必须为 0,其他值会被拒绝
84Route运行时或应用路由 ID
128Request ID请求、响应和错误必须非零;Push 为零
204Sequence会话排序与重连序号
244Payload length帧头之后的字节数
28NPayload帧编解码器视为不透明数据,通常是 protobuf 或 JSON

默认最大载荷为 1 MiB。FrameCodec 允许配置到 64 MiB,但应用应尽量使用更小 的上限。

帧类型

  • Request 发起操作,必须使用非零 Request ID。
  • Response 使用请求的 Request ID、路由和序号进行关联。
  • Push 由服务端发起,Request ID 必须为 0
  • Error 与请求关联,携带序列化错误信封。

WebSocket 传输中,每个二进制 WebSocket Message 必须恰好包含一个 ELR2 帧, 客户端必须协商 elura.v2 子协议。QUIC 同样使用 elura.v2 作为 ALPN。协议 不支持 WebSocket 文本 Message。

Elura 可以生成对应的 C++17、C# 与 TypeScript 编解码器。接入边界和不同传输 方式的检查见客户端协议 SDK

所有语言使用同一套协议

Rust 的 FrameFrameCodec 是 ELR2 的参考实现,而不是 Rust 专用传输协议。 生成的 C++17、C# 和 TypeScript SDK 会编码完全相同的 28 字节帧头、整数字节序、 帧类型、校验规则和 Payload 字节。跨语言黄金向量会验证逐字节兼容性。

存在非 Rust 客户端时,不要把 ELR2 换成原生 Struct 布局、bincode 等 Rust 专用序列化格式;这些格式没有稳定的跨语言 ABI。ELR2 提供语言无关的帧封装, protobuf 提供语言无关的应用请求与响应 Payload。

路由范围

路由含义
1认证
2心跳
3重连
4会话控制
5..99保留给未来运行时功能
100+应用路由

不要为应用分配低于 100 的 ID。Rust 服务端的每个类型化应用路由都实现 Route,在一处绑定 ID、诊断名称、protobuf 请求和 protobuf 响应。 WorldBuilder::register 会在启动时拒绝重复的 ID 和名称。客户端必须使用相同的 路由 ID 与 protobuf Schema。

应用 Payload 与错误

类型化应用 Request 和成功 Response 的 Payload 是应用声明的 protobuf 消息。失败请求使用类型 4,Payload 是规范的 UTF-8 JSON 错误信封:

json
{
  "code": "NOT_ENOUGH_GOLD",
  "message": "金币不足",
  "retryable": false
}

code 只能包含大写 ASCII 字母、数字和下划线,最长 64 字节;message 最长 1024 字节。Error 帧保留原请求的 Route、Request ID 和 Sequence。Error 是请求 结果,因此 Request ID 不能为零;服务端主动通知应使用 Push

认证流程

text
客户端                         Gateway                         World
  │                               │                              │
  ├── 请求 route=1, ticket ─────>│                              │
  │                               ├── 验证签名与 Claims          │
  │                               ├── 检查重放与准入             │
  │<── 响应 session+identity ────┤                              │
  │                               │                              │
  ├── 请求 route>=100 ──────────>│                              │
  │                               ├── 已认证命令 ───────────────>│
  │                               │<── 结果或错误 ───────────────┤
  │<── 响应或错误 ───────────────┤                              │

未认证会话必须在 Gateway 的 authentication_timeout 内完成认证。普通请求还会 受到全局和按路由令牌桶、有界队列、载荷限制、Handler 超时和空闲超时约束。

Request ID 与重试

Request ID 由客户端生成,用于关联请求,且必须非零。Gateway 为近期完成的请求 保留有界响应缓存,World 保留有界幂等缓存。重试应在配置的 TTL 内复用相同的 Request ID 和路由。不要把这些内存缓存当作持久化的 Exactly-once 交付保证。

兼容性

编解码器会拒绝未知 Magic、不支持的版本、未知类型、无效 Request ID、过大载荷 或错误的 Message 长度。协议版本必须显式演进;不要在保留版本 2 的同时改变 现有字段语义。

基于 MIT 许可证发布。