192 lines
6.2 KiB
Markdown
192 lines
6.2 KiB
Markdown
# Protobuf 与协议开发指南
|
||
|
||
`protos/` 是 CMVR-ES 配置、gRPC API、共享消息和 QUIC 控制面的契约源。生成代码由 CMake 创建,不应手工编辑或提交。
|
||
|
||
返回[项目总览](../README.md)。
|
||
|
||
## 目录职责
|
||
|
||
| 目录 | 职责 |
|
||
| --- | --- |
|
||
| `cmvr/api/` | gRPC service 和 command DTO |
|
||
| `cmvr/msgs/` | 设备、状态和错误等共享消息 |
|
||
| `cmvr/config/` | Proto Text 运行配置 Schema |
|
||
| `cmvr/common/` | 通用几何等跨领域消息 |
|
||
| `cmvr/quic_edge/v1/` | QUIC reliable control message 和 v1 wire 文档 |
|
||
| `rbk/` | 第三方/兼容协议 Schema |
|
||
|
||
## C++ 生成流程
|
||
|
||
根 [`CMakeLists.txt`](../CMakeLists.txt):
|
||
|
||
1. 递归收集 `protos/**/*.proto`;
|
||
2. 使用 `protoc` 生成 `.pb.h/.pb.cc`;
|
||
3. 使用 `grpc_cpp_plugin` 生成 `.grpc.pb.h/.grpc.pb.cc`;
|
||
4. 输出到 `build/_protobuf/`;
|
||
5. 打包为 `cmvr_es::proto`。
|
||
|
||
`build/_protobuf/` 是构建产物,不手改、不提交。
|
||
|
||
当前使用 `file(GLOB_RECURSE ...)` 且没有 `CONFIGURE_DEPENDS`。新增 Proto 文件后必须重新执行 CMake configure:
|
||
|
||
```bash
|
||
cmake -S . -B build \
|
||
-DCMAKE_BUILD_TYPE=Release \
|
||
-DCMVR_ARCH=x86
|
||
|
||
cmake --build build -j"$(nproc)"
|
||
```
|
||
|
||
`protoc` 和 `grpc_cpp_plugin` 当前固定从 `output/bin/` 使用。干净环境首次构建先执行根 README 的工具引导步骤。
|
||
|
||
## 通用兼容规则
|
||
|
||
已发布协议中禁止:
|
||
|
||
- 修改或复用字段号;
|
||
- 修改 enum 数值含义;
|
||
- 修改 oneof tag;
|
||
- 修改字段 wire type;
|
||
- 修改 gRPC package、service 或 method 全名;
|
||
- 将原本可选的字段改成必填语义;
|
||
- 把同一字段单位从 m 改成 mm 等隐式破坏。
|
||
|
||
删除字段时同时 reserved 字段号和名称:
|
||
|
||
```protobuf
|
||
message Example {
|
||
reserved 2;
|
||
reserved "old_field";
|
||
|
||
string id = 1;
|
||
string new_field = 3;
|
||
}
|
||
```
|
||
|
||
新增字段使用新 tag。需要区分“缺失”和“零值”时使用 proto3 `optional`,并验证目标语言工具链支持。
|
||
|
||
破坏性 gRPC 变化应创建版本化 package/service,而不是原地改变旧方法。
|
||
|
||
## 配置 Proto
|
||
|
||
配置也必须考虑已部署 `.pb.txt`:
|
||
|
||
- 新 scalar 的零值不能自动成为危险默认值;
|
||
- C++ 必须做范围和跨字段校验;
|
||
- enum unknown、oneof 未设置应明确失败;
|
||
- root message 命名保持 `XxxRootConfig`;
|
||
- 字段注释写明单位、范围和安全语义;
|
||
- Schema 变更同步 [`../cmvr-es/config/README.md`](../cmvr-es/config/README.md) 与样例配置。
|
||
|
||
TextFormat 使用字段名,因此随意重命名字段同样会破坏旧配置。
|
||
|
||
## gRPC API
|
||
|
||
推荐一个领域拆分为:
|
||
|
||
```text
|
||
cmvr/api/example_command.proto
|
||
cmvr/api/example_service.proto
|
||
```
|
||
|
||
service 文件 import command 文件。公共 header、错误和时间戳复用 `common.proto`,不要复制出多个略有差异的定义。
|
||
|
||
新增 API 后还需要:
|
||
|
||
1. 实现 C++ service;
|
||
2. 加入 service CMake target;
|
||
3. 在 GrpcServerTask 中构造和注册;
|
||
4. 生成 Java client;
|
||
5. 增加 handler、reflection 和 grpcurl 测试。
|
||
|
||
完整流程见 [`../cmvr-es/service/README.md`](../cmvr-es/service/README.md)。
|
||
|
||
`cmvr/api/test_service.proto` 当前没有实现或注册,且包含历史拼写 `TestReqeust`,不要把它作为新 API 模板。
|
||
|
||
## QUIC Edge v1
|
||
|
||
当前固定契约:
|
||
|
||
- package:`cmvr.quic_edge.v1`
|
||
- `protocol_version = 1`
|
||
- ALPN:`cmvr-quic-edge/1`
|
||
- reliable stream:`uint32_be length + EdgeControlEnvelope`
|
||
- media DATAGRAM:固定 64 字节非 Protobuf header + payload
|
||
|
||
完整字段和偏移见 [`cmvr/quic_edge/v1/README.md`](cmvr/quic_edge/v1/README.md)。
|
||
|
||
破坏 framing、固定头、状态机或时序时,必须建立新协议版本、package 和 ALPN,不能只修改 Proto。
|
||
|
||
新增 control envelope payload 时:
|
||
|
||
- 使用未占用的新 oneof tag;
|
||
- 同步 edge 发送/dispatch 和状态机;
|
||
- 同步 message/session/heartbeat sequence 校验;
|
||
- 同步 Java Gateway;
|
||
- 更新 wire 文档;
|
||
- 增加 golden vector 和 fake transport 测试。
|
||
|
||
Edge 当前入站只实现 RegisterResponse、HeartbeatAck 和 ProtocolError。Proto 中存在的消息不等于两个方向都已实现。
|
||
|
||
每次 QUIC 连接建立后,Edge 的出站 control `message_sequence` 从 `0` 重新开始,因此首个 `NodeRegisterRequest.message_sequence` 当前为 `0`。Gateway 必须接受该起始值,不能假设从 `1` 开始;后续只要求同一发送方向严格递增,允许出现间隔。
|
||
|
||
QUIC stream 与 DATAGRAM 没有跨通道到达顺序保证。Gateway 不能假设 session/descriptor 一定先于媒体到达。
|
||
|
||
## Java 生成
|
||
|
||
仓库只自动生成 C++。Java 平台应在自己的 Gradle/Maven 工程中锁定:
|
||
|
||
- protoc
|
||
- protobuf-java
|
||
- protoc-gen-grpc-java
|
||
- grpc-java
|
||
|
||
并以 `protos/` 作为 import root。
|
||
|
||
QUIC Gateway 只需普通 Protobuf Java message:
|
||
|
||
```bash
|
||
output/bin/protoc \
|
||
-I protos \
|
||
--java_out=platform-gateway/src/main/java \
|
||
protos/cmvr/quic_edge/v1/quic_edge.proto
|
||
```
|
||
|
||
gRPC Java client 还需要 grpc-java plugin。当前 Proto 没有统一 `java_package` 和 `java_multiple_files`,增加这些 option 时应同步两端生成结果和平台 import。
|
||
|
||
`cmvr.api.FrameData` 的字段 7–13 是可选的实时流诊断信息,包括边缘端采集 UTC
|
||
时间、源序列、PTS/DTS、SDK 帧率、SDK 原始时间戳和帧号。旧客户端会安全忽略
|
||
这些字段;Java 平台需要重新生成 message 和 stub 才能读取它们。SDK 原始时间戳
|
||
的单位和时钟域由设备定义,不能按 Unix 时间直接解释。
|
||
|
||
浏览器不能直接消费自定义 QUIC ALPN,不应从这些 Proto 推导“浏览器可直连 Edge”。
|
||
|
||
## Review 与验证
|
||
|
||
```bash
|
||
# 重新生成并编译 C++
|
||
cmake -S . -B build \
|
||
-DCMAKE_BUILD_TYPE=Release \
|
||
-DCMVR_ARCH=x86
|
||
cmake --build build -j"$(nproc)"
|
||
|
||
# 验证当前自动测试
|
||
ctest --test-dir build --output-on-failure
|
||
|
||
# 验证运行时 reflection
|
||
/tmp/grpcurl -plaintext 127.0.0.1:50052 list
|
||
```
|
||
|
||
评审清单:
|
||
|
||
- [ ] 新字段只使用新 tag
|
||
- [ ] 删除字段同时 reserved number 和 name
|
||
- [ ] enum 数值和语义兼容
|
||
- [ ] 旧 pb.txt 仍可解析
|
||
- [ ] C++ 重新 configure 并生成
|
||
- [ ] Java/其他语言生成成功
|
||
- [ ] service 已真正注册
|
||
- [ ] grpcurl reflection 与预期一致
|
||
- [ ] QUIC wire 文档和测试同步更新
|
||
- [ ] 破坏性变化采用新版本
|