chore: upload current project state
This commit is contained in:
@@ -0,0 +1,355 @@
|
||||
# WebSocket 协议改造分析
|
||||
|
||||
日期:2026-05-11
|
||||
|
||||
参考文档:
|
||||
- `https://gitea.s5s8.com/anlicheng/ekfa/raw/branch/protobuf_mst/docs/service_websocket_protocol.md`
|
||||
|
||||
## 1. 当前项目的通讯方式
|
||||
|
||||
当前实现文件:
|
||||
- `connect/ws_channel.go`
|
||||
- `reporter/reporter.go`
|
||||
- `main.go`
|
||||
|
||||
当前协议特征:
|
||||
|
||||
1. 传输层
|
||||
- WebSocket 文本帧
|
||||
- 使用 JSON 编码
|
||||
|
||||
2. 请求响应模型
|
||||
- 统一消息结构:`{id, method, params, result, error}`
|
||||
- 当前请求方法主要有:
|
||||
- `register`
|
||||
- `subscribe`
|
||||
- 当前推送/上报主要有:
|
||||
- `metric_data`
|
||||
- `publish`
|
||||
|
||||
3. 注册逻辑
|
||||
- 客户端连接到 `ws://172.17.0.1:18080/ws`
|
||||
- 发送 `register`
|
||||
- 参数包含:
|
||||
- `container_name`
|
||||
- `service_id`
|
||||
- `meta_data`
|
||||
|
||||
4. 订阅逻辑
|
||||
- 发送 `subscribe`
|
||||
- 参数为:
|
||||
- `topic`
|
||||
|
||||
5. 下行消息
|
||||
- 服务端下发 `publish`
|
||||
- `params` 中当前代码按如下字段解析:
|
||||
- `topic`
|
||||
- `data`
|
||||
- `content`
|
||||
|
||||
6. 上行数据上报
|
||||
- 业务侧通过:
|
||||
- `method = "metric_data"`
|
||||
- `params.route_key`
|
||||
- `params.metric`
|
||||
- `reporter` 中事件与数据快照上报都走这个逻辑
|
||||
|
||||
7. 心跳与重连
|
||||
- 客户端主动发 WebSocket ping frame
|
||||
- 自己维护重连、重注册、重订阅
|
||||
|
||||
## 2. 目标协议的通讯方式
|
||||
|
||||
根据新文档,目标协议特征如下:
|
||||
|
||||
1. 传输层
|
||||
- WebSocket binary frame
|
||||
- 每帧格式:
|
||||
- `1 byte frame_type`
|
||||
- `N bytes protobuf payload`
|
||||
|
||||
2. 帧类型
|
||||
- `0x01`:`REQUEST`
|
||||
- `0x02`:`REPLY`
|
||||
- `0x03`:`CAST`
|
||||
|
||||
3. protobuf 消息
|
||||
- `ServiceRequest`
|
||||
- `ServiceReply`
|
||||
- `ServiceCast`
|
||||
|
||||
4. request/reply
|
||||
- `register`
|
||||
- 参数只有 `service_id`
|
||||
- `subscribe`
|
||||
- 参数只有 `topic`
|
||||
- `reply`
|
||||
- 成功:`result = "ok"`
|
||||
- 失败:`error.code = -1`
|
||||
|
||||
5. cast
|
||||
- service -> efka
|
||||
- `metric_data`
|
||||
- 字段:
|
||||
- `route_key: bytes`
|
||||
- `metric: bytes`
|
||||
- efka -> service
|
||||
- `topic_event`
|
||||
- 字段:
|
||||
- `topic: string`
|
||||
- `content: bytes`
|
||||
|
||||
6. 注册前约束
|
||||
- 未注册前只允许 `register`
|
||||
- 其它 request 返回 `invalid request`
|
||||
- cast 会被忽略
|
||||
|
||||
7. ping/pong
|
||||
- Cowboy 直接处理 ping/pong
|
||||
- service 侧无需再设计额外业务层心跳协议
|
||||
|
||||
## 3. 当前与目标协议的核心差异
|
||||
|
||||
## 编码层差异
|
||||
|
||||
当前:
|
||||
- JSON
|
||||
- 文本帧
|
||||
|
||||
目标:
|
||||
- protobuf
|
||||
- 二进制帧
|
||||
|
||||
影响:
|
||||
- `connect/ws_channel.go` 的读写逻辑要整体重写,不能局部修补
|
||||
|
||||
## 消息模型差异
|
||||
|
||||
当前:
|
||||
- 通用 JSON RPC 风格:`id + method + params/result/error`
|
||||
|
||||
目标:
|
||||
- 分离为 `REQUEST / REPLY / CAST`
|
||||
- 且由帧头字节决定消息类型
|
||||
|
||||
影响:
|
||||
- 现有 `WSMessage` 结构体要废弃或降级为兼容层
|
||||
|
||||
## 注册参数差异
|
||||
|
||||
当前:
|
||||
- `register(container_name, service_id, meta_data)`
|
||||
|
||||
目标:
|
||||
- `register(service_id)`
|
||||
|
||||
影响:
|
||||
- `RegisterParams` 需要改
|
||||
- `container_name`、`meta_data` 不应继续按当前协议发送
|
||||
|
||||
## 下行消息差异
|
||||
|
||||
当前:
|
||||
- 服务端下发 `publish`
|
||||
- 业务代码把 `publish.params` 再反序列化
|
||||
|
||||
目标:
|
||||
- 服务端下发 `CAST topic_event`
|
||||
|
||||
影响:
|
||||
- `handlePublishMessage()` 这类 JSON `publish` 解析流程要删除
|
||||
- 新增 `topic_event` 的 protobuf 解码与路由
|
||||
|
||||
## 上行上报差异
|
||||
|
||||
当前:
|
||||
- `method = metric_data`
|
||||
- `params.route_key`
|
||||
- `params.metric`
|
||||
|
||||
目标:
|
||||
- `CAST metric_data`
|
||||
- `route_key` 和 `metric` 都是 `bytes`
|
||||
|
||||
影响:
|
||||
- `reporter` 发消息时不再构造 JSON RPC
|
||||
- 要构造 `ServiceCast.metric_data`
|
||||
|
||||
## reply 相关差异
|
||||
|
||||
当前:
|
||||
- 请求响应靠 JSON 的 `id`
|
||||
|
||||
目标:
|
||||
- `packet_id`
|
||||
- 且只存在于 `ServiceRequest / ServiceReply`
|
||||
|
||||
影响:
|
||||
- 当前 `callbacks map[int64]chan *WSMessage` 机制可以保留思路
|
||||
- 但要替换成 protobuf `packet_id` 和 `ServiceReply`
|
||||
|
||||
## 4. 当前项目中需要改动的代码点
|
||||
|
||||
## A. `connect/ws_channel.go` 需要整体重构
|
||||
|
||||
需要改动:
|
||||
|
||||
1. 连接保持不变
|
||||
- 仍然连 `/ws`
|
||||
|
||||
2. 写消息逻辑
|
||||
- 现在 `SendRawMessage()` 直接 `json.Marshal()` 后发 `TextMessage`
|
||||
- 需要改成:
|
||||
- protobuf 序列化
|
||||
- 前面拼接 `frame_type`
|
||||
- 发 `BinaryMessage`
|
||||
|
||||
3. 请求发送逻辑
|
||||
- 现在 `sendRequest(method, params, waitResponse)` 是 JSON 风格
|
||||
- 需要改成:
|
||||
- 组 `ServiceRequest`
|
||||
- 填 `packet_id`
|
||||
- body 选择 `register` 或 `subscribe`
|
||||
- 发 `0x01 + protobuf(ServiceRequest)`
|
||||
|
||||
4. 读消息逻辑
|
||||
- 现在 `readLoop()` 直接把整帧当 JSON 解码
|
||||
- 需要改成:
|
||||
- 读取二进制帧
|
||||
- 取第一个字节判断帧类型
|
||||
- `0x02` 解码为 `ServiceReply`
|
||||
- `0x03` 解码为 `ServiceCast`
|
||||
|
||||
5. 下行消息分发
|
||||
- 现在按 `message.Method == "publish"` 处理
|
||||
- 需要改成按 `ServiceCast.body`
|
||||
- `topic_event`
|
||||
- 后续如协议扩展再加别的 cast
|
||||
|
||||
6. 回调等待机制
|
||||
- 现在按 `id` 关联回调
|
||||
- 需要改成按 `packet_id`
|
||||
|
||||
## B. 注册逻辑需要调整
|
||||
|
||||
当前:
|
||||
- `RegisterService(containerName string)`
|
||||
- 发送 `container_name + service_id + meta_data`
|
||||
|
||||
目标:
|
||||
- 仅发送 `service_id`
|
||||
|
||||
建议:
|
||||
- 把 `container_name` 从协议层移除
|
||||
- `service_id` 建议继续使用当前生成逻辑,但需确认上层是否有固定命名要求
|
||||
|
||||
## C. 订阅逻辑需要调整
|
||||
|
||||
当前:
|
||||
- 发送 JSON `subscribe`
|
||||
|
||||
目标:
|
||||
- 发送 protobuf `ServiceRequest.Subscribe`
|
||||
|
||||
建议:
|
||||
- `SubscribeTopic()` 对外接口可以保留
|
||||
- 内部实现改为 protobuf request
|
||||
|
||||
## D. 上报逻辑需要调整
|
||||
|
||||
当前:
|
||||
- `reporter.ReportEvent()`
|
||||
- `reporter.ReportDataSnapshot()`
|
||||
- `GenerateEventMessage()`
|
||||
- `GenerateDataMessage()`
|
||||
- 最终都构造成 JSON `metric_data`
|
||||
|
||||
目标:
|
||||
- 最终发送 protobuf `ServiceCast.metric_data`
|
||||
|
||||
建议:
|
||||
- `GenerateEventMessage()` / `GenerateDataMessage()` 不一定保留 map 结构
|
||||
- 更合理的是:
|
||||
- 先生成业务 payload JSON 字符串
|
||||
- 再把该字符串作为 `metric_data.metric` 的 bytes 内容
|
||||
- `route_key` 也按 bytes 写入 protobuf
|
||||
|
||||
## E. 下行配置/告警处理需要调整
|
||||
|
||||
当前:
|
||||
- 依赖 `publish.params.topic/data/content`
|
||||
- 并把 `data` 当 `DeviceData` 解析
|
||||
|
||||
目标:
|
||||
- 依赖 `topic_event.topic/content`
|
||||
- `content` 是 bytes
|
||||
|
||||
建议:
|
||||
- 重新梳理每个 topic 的 payload 格式
|
||||
- 尤其要确认:
|
||||
- `/dhlr/alert`
|
||||
- `/dhlr/forbid_time`
|
||||
- 设备配置下发 topic
|
||||
- 不能再默认存在 `publish.data`
|
||||
|
||||
## 5. 改造时需要确认的外部问题
|
||||
|
||||
这些问题代码里无法自行推断,需要和上层协议一起确认:
|
||||
|
||||
1. 设备配置下发现在走哪个 topic
|
||||
- 当前代码默认任何 `publish.data` 都可能是 `DeviceData`
|
||||
- 新协议文档只定义了 `topic_event(topic, content)`,没定义具体业务 topic 的 payload schema
|
||||
|
||||
2. `content` 的业务内容格式是什么
|
||||
- 是 JSON bytes
|
||||
- 还是 protobuf bytes
|
||||
- 还是普通字符串
|
||||
|
||||
3. `service_id` 的命名规则
|
||||
- 现有代码自动生成 `fireleave-service-<timestamp>`
|
||||
- 新链路是否要求固定 `service_id`
|
||||
|
||||
4. topic 命名是否需要改
|
||||
- 当前项目使用:
|
||||
- `/dhlr/alert`
|
||||
- `/dhlr/forbid_time`
|
||||
- `/dhlr/device/<uuid>/event`
|
||||
- `/dhlr/device/<uuid>/data`
|
||||
- 文档中的示例 topic 风格更像:
|
||||
- `device/a/event`
|
||||
- `device/*/event`
|
||||
- 需要确认上层真实路由规范
|
||||
|
||||
5. 是否还需要客户端主动 ping
|
||||
- 文档说明服务端会直接回复 ping/pong
|
||||
- 但没有明确要求客户端是否必须主动发 ping
|
||||
- 当前保留主动 ping 一般没问题,但需要确认是否有重复机制
|
||||
|
||||
## 6. 建议的改造顺序
|
||||
|
||||
1. 先生成 `service.proto` 的 Go 代码
|
||||
2. 新建独立的 protobuf WebSocket 客户端层
|
||||
3. 保留现有上层接口:
|
||||
- `RegisterService`
|
||||
- `SubscribeTopic`
|
||||
- `SendAsync`
|
||||
4. 先把协议层替换掉,不动业务层调用点
|
||||
5. 再改下行 `topic_event` 到业务 topic 的解析分发
|
||||
6. 最后联调配置下发、事件上报、重连重订阅
|
||||
|
||||
## 7. 对当前项目的直接结论
|
||||
|
||||
这次协议改造不是小修小补,而是通讯层整体替换,最核心的改动文件会是:
|
||||
|
||||
- `connect/ws_channel.go`
|
||||
- `reporter/reporter.go`
|
||||
- 可能新增:
|
||||
- `connect/protocol/`
|
||||
- `connect/pb/`
|
||||
|
||||
同时,当前项目里“配置更新不生效”的问题建议和这次协议改造一起处理,否则即使新协议打通,设备配置下发链路也还是不完整。
|
||||
|
||||
|
||||
1.我的业务逻辑是这样的,我是末端的微服务,我负责采集数据然后上报数据,我的业务是安防的,我这边分析发现危险情况然后上报给上层,而forbid_time和alert是我联动的硬件侧的声光报警器,他们分别区分的是报警间隔时间和报警信息。我对你这句话的理解,你认为topic是对多个微服务或者设备的一个共同网络,如果改动了topic_event(topic, content)我订阅这个topic的所有设备都会触发声光报警是吗?
|
||||
2.对的,主要是外层传输协议的改动。
|
||||
3.可能是你不清楚现在的运行环境,我先描述一下现在的业务环境,
|
||||
Reference in New Issue
Block a user