更新 状态机.md

This commit is contained in:
liangqiang 2026-04-27 14:23:41 +08:00
parent d441f0ec13
commit 03ea576e68

View File

@ -1,183 +1,211 @@
状态机就是“把一次视频会话/设备流程拆成明确状态,并规定状态之间怎么流转”的机制。 ```markdown
device-bootstrap 就是“设备启动或上线时,先来云端报到、认证,并领取连接 MQTT/实时系统所需临时配置和凭证”的接入服务。 # 状态机与 `device-bootstrap` 术语说明
1. 状态机是什么意思 ## 一、核心结论
1.1 通俗解释
状态机可以理解成一张流程规则表。
比如一次智能锁视频通话,不应该只是简单地说: **状态机**:用于管理一次视频会话或设备流程的状态变化,明确规定当前处于什么状态、下一步能进入什么状态、异常时如何处理。
**`device-bootstrap`**:设备启动或上线时访问的接入服务,用于完成设备认证,并下发 MQTT 临时凭证、topic 权限、连接配置和设备能力配置。
---
## 二、状态机是什么意思
### 1. 通俗理解
状态机可以理解成一张**流程规则表**。
比如一次智能锁视频通话,不应该只有:
```text
开始通话 开始通话
结束通话 结束通话
而是应该有一系列明确状态: ```
而应该有完整流程:
```text
待创建 -> 呼叫中 -> 已接听 -> 连接中 -> 已连接 -> 已结束
```
待创建 -> 呼叫中 -> 已接听 -> 连接中 -> 已连接 -> 通话中 -> 已结束
每个状态只能按照规则进入下一个状态。 每个状态只能按照规则进入下一个状态。
例如: 例如:
```text
pending -> ringing -> accepted -> connecting -> connected -> ended pending -> ringing -> accepted -> connecting -> connected -> ended
不能乱跳,比如: ```
不能随意跳转,例如:
```text
pending -> connected pending -> connected
通常就不合理,因为中间还没经过设备接听和媒体连接。 ```
1.2 在你这个智能锁项目里的含义 通常是不合理的,因为中间还没有经过设备接听、媒体连接等步骤。
你们旧方案里可能是通过 UDP 字段告诉 APP 或锁:
---
## 三、状态机在智能锁项目中的作用
旧方案里,可能通过 UDP 字段直接通知 APP 或锁:
```text
开始视频 开始视频
结束视频 结束视频
这种方式简单,但问题是: ```
APP 以为开始了,设备没收到 这种方式简单,但容易出现以下问题:
设备接听了APP 已经取消 - APP 以为开始了,设备没收到
- 设备接听了APP 已经取消
- 网络抖动导致消息重复
- 云端不知道当前到底是“呼叫中”还是“已连接”
- 通话失败后不好追踪原因
网络抖动导致消息重复 新方案中,由 `session-orchestrator` 统一管理状态机。
云端不知道现在到底是“呼叫中”还是“已连接”
通话失败后不好追踪原因
所以新方案里要用 session-orchestrator 管理状态机。
也就是云端统一记录: 也就是云端统一记录:
```text
这个 sessionId 当前到底处于什么状态 这个 sessionId 当前到底处于什么状态
2. 视频会话状态机例子 ```
2.1 推荐状态
pending
ringing
accepted
connecting
connected
degraded
switching_route
recovered
ended
rejected
timeout
failed
可以翻译成:
状态 ---
中文含义 ## 四、视频会话状态机示例
pending ### 1. 推荐状态
会话刚创建,等待发起呼叫 | 状态 | 中文含义 |
|---|---|
| `pending` | 会话刚创建,等待发起呼叫 |
| `ringing` | 正在呼叫设备 / 设备响铃 |
| `accepted` | 设备已接听 |
| `connecting` | APP 和设备正在建立媒体连接 |
| `connected` | 音视频已连通 |
| `degraded` | 通话质量下降 |
| `switching_route` | 正在切换媒体线路 |
| `recovered` | 切换后恢复正常 |
| `ended` | 正常结束 |
| `rejected` | 被拒接 |
| `timeout` | 超时未响应 |
| `failed` | 异常失败 |
ringing ---
正在呼叫设备 / 设备响铃 ### 2. 正常通话流程
accepted
设备已接听
connecting
APP 和设备正在建立媒体连接
connected
音视频已连通
degraded
通话质量下降
switching_route
正在切换媒体线路
recovered
切换后恢复正常
ended
正常结束
rejected
被拒接
timeout
超时未响应
failed
异常失败
2.2 状态流转例子
一次正常视频通话:
```text
pending pending
-> ringing -> ringing
-> accepted -> accepted
-> connecting -> connecting
-> connected -> connected
-> ended -> ended
中文就是: ```
对应中文流程:
```text
创建会话 创建会话
-> 呼叫设备 -> 呼叫设备
-> 设备接听 -> 设备接听
-> 建立 WebRTC / ZLM 连接 -> 建立 WebRTC / ZLM 连接
-> 通话成功 -> 通话成功
-> 挂断结束 -> 挂断结束
2.3 异常情况例子 ```
设备不在线
---
### 3. 异常流程示例
#### 设备不在线
```text
pending -> failed pending -> failed
原因: ```
失败原因:
```text
device_offline device_offline
设备没有响应 ```
#### 设备没有响应
```text
pending -> ringing -> timeout pending -> ringing -> timeout
原因: ```
失败原因:
```text
invite_timeout invite_timeout
用户取消 ```
#### 用户取消
```text
pending -> ringing -> ended pending -> ringing -> ended
原因: ```
结束原因:
```text
caller_cancelled caller_cancelled
设备拒接 ```
pending -> ringing -> rejected
原因:
#### 设备拒接
```text
pending -> ringing -> rejected
```
失败原因:
```text
device_rejected device_rejected
WebRTC 连接失败,切 ZLMediaKit ```
#### WebRTC 连接失败后切换 ZLMediaKit
```text
pending pending
-> ringing -> ringing
-> accepted -> accepted
-> connecting -> connecting
-> failed_p2p
-> switching_route
-> connected
或者更规范一点:
connecting
-> degraded -> degraded
-> switching_route -> switching_route
-> recovered -> recovered
-> connected -> connected
3. 为什么一定要状态机 ```
3.1 保证流程不乱
---
## 五、为什么需要状态机
### 1. 保证流程不乱
没有状态机时,可能出现: 没有状态机时,可能出现:
```text
已经挂断了,又收到设备 accept 已经挂断了,又收到设备 accept
那系统不知道该怎么办。 ```
有状态机后可以规定: 有状态机后可以规定:
```text
ended 状态下收到 accept直接忽略或记录为迟到事件 ended 状态下收到 accept直接忽略或记录为迟到事件
3.2 方便排查问题 ```
有状态机后,每个会话都能查:
---
### 2. 方便排查问题
有状态机后,可以按 `sessionId` 查完整流程:
```text
sessionId = abc123 sessionId = abc123
pending at 10:00:01 pending at 10:00:01
ringing at 10:00:02 ringing at 10:00:02
@ -185,21 +213,344 @@ accepted at 10:00:05
connecting at 10:00:06 connecting at 10:00:06
timeout at 10:00:16 timeout at 10:00:16
reason = ice_timeout reason = ice_timeout
这样就能知道失败原因是: ```
设备接了,但 WebRTC 没连上 这样可以明确知道失败原因是:
而不是一句模糊的:
```text
设备接听了,但 WebRTC 没连上
```
而不是笼统地显示:
```text
通话失败 通话失败
3.3 方便做超时和重试 ```
比如:
呼叫设备 15 秒没响应,自动超时 ---
WebRTC 10 秒没连上,切 ZLM ### 3. 方便做超时和重试
设备 ACK 没回来,重发 invite 状态机可以支持:
通话结束后清理 token 和流资源 - 呼叫设备 15 秒没响应,自动超时
- WebRTC 10 秒没连上,切换 ZLMediaKit
- 设备 ACK 没回来,重发 invite
- 通话结束后清理 token 和流媒体资源
这些都依赖状态机。 ---
## 六、`device-bootstrap` 是什么意思
### 1. 通俗理解
`device-bootstrap` 可以理解为:
> 设备上线入口 / 设备启动接入服务 / 设备报到服务
设备开机后,不应该直接随便连接 MQTT也不应该把固定账号密码写死在固件里。
更安全的做法是:
```text
设备开机
-> 调用 device-bootstrap
-> 云端验证设备身份
-> 云端返回 MQTT 地址、临时账号、topic 权限、配置
-> 设备再连接 BifroMQ
```
---
## 七、`device-bootstrap` 在项目中的作用
### 1. 设备启动时的流程
设备,也就是 WiFi 网关锁,启动后先请求:
```http
POST /device/bootstrap
```
请求中通常携带:
```json
{
"deviceId": "device_001",
"firmwareVersion": "1.0.0",
"timestamp": 1710000000,
"signature": "xxxxx",
"capabilities": {
"webrtc": true,
"zlm": true,
"ai": true
}
}
```
云端验证通过后返回:
```json
{
"mqtt": {
"host": "mqtt.example.com",
"port": 8883,
"clientId": "lock_xxx",
"username": "tmp_user",
"password": "tmp_password",
"expiresIn": 3600
},
"topics": {
"subscribe": [
"tenant/t1/device/d1/control",
"tenant/t1/session/+/signal/device"
],
"publish": [
"tenant/t1/device/d1/status",
"tenant/t1/device/d1/event",
"tenant/t1/session/+/signal/app"
]
},
"config": {
"heartbeatInterval": 30,
"webrtcEnabled": true,
"zlmEnabled": true,
"aiEnabled": true
}
}
```
含义是:
- 设备认证通过
- 返回 MQTT 连接地址
- 返回临时账号密码
- 返回允许订阅的 topic
- 返回允许发布的 topic
- 返回心跳周期
- 返回设备功能开关配置
---
## 八、`device-bootstrap` 解决的问题
### 1. 避免设备写死 MQTT 密码
不推荐:
```text
所有设备固件里写同一个 MQTT 用户名和密码
```
风险是:一旦泄露,所有设备都会受影响。
推荐:
```text
每台设备启动时领取自己的临时凭证
```
---
### 2. 控制设备权限
例如设备 `device_001` 只能访问:
```text
tenant/t1/device/device_001/...
```
不能访问:
```text
tenant/t1/device/device_002/...
```
---
### 3. 支持设备配置下发
例如某个设备支持 WebRTC
```json
{
"webrtcEnabled": true
}
```
另一个旧设备不支持:
```json
{
"webrtcEnabled": false
}
```
系统就可以按设备能力下发不同策略。
---
### 4. 支持灰度升级
例如只让 10% 的设备启用 ZLMediaKit
```json
{
"zlmEnabled": true
}
```
其它设备暂时不开启。
---
## 九、`device-bootstrap``session-orchestrator` 的区别
| 模块 | 作用 | 类比 |
|---|---|---|
| `device-bootstrap` | 设备上线认证、领取 MQTT 凭证、获取配置 | 门卫 / 报到处 |
| `session-orchestrator` | 管理一次视频会话的创建、呼叫、接听、连接、切路、挂断 | 调度员 / 指挥中心 |
| BifroMQ | 负责消息通道 | 对讲机频道 / 消息总线 |
| ZLMediaKit / WebRTC | 负责音视频传输 | 真正的视频通话线路 |
简单理解:
```text
device-bootstrap 管设备怎么接入系统
session-orchestrator 管一次通话怎么开始、进行、结束
```
---
## 十、完整流程<E6B581><E7A88B><EFBFBD>
### 1. 设备上线流程
```text
WiFi锁开机
-> 调用 device-bootstrap
-> bootstrap 验证设备身份
-> 返回 MQTT 临时凭证
-> WiFi锁连接 BifroMQ
-> WiFi锁上报 online
```
这部分由 `device-bootstrap` 负责。
---
### 2. 用户发起视频通话流程
```text
APP 点击视频通话
-> 调用 starlock
-> starlock 校验用户是否有权限访问这把锁
-> starlock 调用 session-orchestrator 创建 session
-> session-orchestrator 通过 BifroMQ 给设备发 invite
-> 设备响铃并返回 accept
-> APP 和设备交换 WebRTC signaling
-> 建立 P2P / TURN / ZLM 媒体链路
```
这部分由 `session-orchestrator` 负责。
---
### 3. 通话中弱网切换流程
```text
APP 上报丢包高
设备上报 RTT 高
session-orchestrator 判断质量差
-> 下发 switch-route
-> APP 和设备切到 ZLMediaKit
```
这仍然属于状态机控制。
---
## 十一、<E4B880><E38081><EFBFBD>团队的简短解释
### 状态机一句话
> 状态机就是云端统一管理“这次通话现在进行到哪一步、下一步允许做什么、超时或异常怎么处理”的规则系统。
### `device-bootstrap` 一句话
> `device-bootstrap` 就是设备开机后的接入报到服务,负责认证设备,并给设备下发 MQTT 临时凭证、topic 权限和能力配置。
---
## 十二、项目落地建议
### 1. 状态机第一版建议
第一版状态不要太复杂,建议先支持:
```text
created
ringing
accepted
connecting
connected
ended
rejected
timeout
failed
```
后续稳定后再增加:
```text
degraded
switching_route
recovered
```
---
### 2. `device-bootstrap` 第一版必须返回
```text
mqttHost
mqttPort
clientId
username
password
expiresIn
subscribeTopics
publishTopics
heartbeatInterval
capabilities
```
不要让设备直接写死 MQTT 账号密码。
---
## 十三、最简对照表
| 模块 | 负责内容 |
|---|---|
| 状态机 | 管“通话流程” |
| `device-bootstrap` | 管“设备上线接入” |
| BifroMQ | 管“消息通道” |
| WebRTC / ZLM | 管“视频声音怎么传” |
| `starlock` | 管“用户有没有权限” |
| `starcloud` | 管“共享业务能力” |
---
## 十四、总结
在智能锁实时系统中:
```text
用户有没有权限开视频starlock / starcloud 判断
设备怎么连上云device-bootstrap
控制消息怎么传BifroMQ
这次通话现在到哪一步session-orchestrator 状态机
视频音频怎么传WebRTC / TURN / ZLMediaKit
```
```