更新与重启
Linux 部署以 launch.sh 为入口。它是常驻父进程,负责运行发布产物、识别程序请求的动作,并在需要时重建或切换发布槽。
日常操作
| 目标 | 操作 | 结果 |
|---|---|---|
| 正常启动 | ./launch.sh | 启动活动槽;首次运行会先构建 |
| 强制重建 | ./launch.sh -f | 重建活动槽后启动 |
| 不编译重启 | WebUI「重载程序」或群聊 @机器人 /reload | 当前槽直接再次启动 |
| 编译后重启 | WebUI「重启程序」 | 重建当前槽后启动 |
| 拉取并更新 | WebUI「更新版本」或授权用户执行 @机器人 /update [-f] | 构建备用槽并切换 |
群聊的 @机器人 /update、@机器人 /reload 会校验 AuthorizedUser。WebUI 没有身份鉴别,只能由可信本机用户访问;访问边界见日志与数据。
launch.sh 做什么
脚本管理 build/slot_a 和 build/slot_b 两个发布目录,build/active_slot 只保存当前要启动的槽名。
启动时,它读取活动槽;槽不存在时调用 build.sh 发布,然后执行该目录内的 MerryBot。程序结束后,脚本根据退出码决定下一步,而不是无条件重启。
| 退出码 | 程序请求 | 脚本动作 |
|---|---|---|
| 101 | 重启 | 重建当前活动槽,再启动 |
| 102 | 重载 | 不编译,直接再次启动当前槽 |
| 103 | 使用预构建版本 | 重新读取 active_slot,启动已准备好的备用槽 |
| 其他 | 正常退出或故障 | 结束 launch.sh |
直接运行发布目录中的 MerryBot 或 dotnet run 不会处理这些退出码,因此 Linux 服务应由 ./launch.sh 托管。
状态机
stateDiagram-v2
[*] --> 读取活动槽
读取活动槽 --> 检查发布产物
检查发布产物 --> 运行程序: 槽已构建
检查发布产物 --> 构建活动槽: 首次启动或 -f
构建活动槽 --> 运行程序: 构建成功
构建活动槽 --> [*]: 构建失败
运行程序 --> 构建活动槽: 退出码 101(重启)
运行程序 --> 运行程序: 退出码 102(重载)
运行程序 --> 读取活动槽: 退出码 103(切换预构建槽)
运行程序 --> [*]: 其他退出码
A/B 双槽更新
双槽思路类似 Android A/B 更新:运行中的槽不动,新版本先写入另一个槽,成功后才切换入口。
假设当前活动槽是 A:
HostLifecycle获取 git 更新,在未运行的槽 B 调用build.sh。- 构建期间,槽 A 的机器人继续运行。
- 槽 B 构建成功后,程序写入
build/active_slot = B,再以退出码 103 结束。 launch.sh读取新的活动槽并启动 B;槽 A 的旧发布产物保留。
| 时机 | 槽 A | 槽 B | active_slot |
|---|---|---|---|
| 更新前 | 正在运行 | 上次发布产物或空 | A |
| 构建中 | 继续运行,不改动 | 发布新版本 | A |
| 构建成功 | 保留旧版本 | 新版本已就绪 | 切为 B |
| 重启后 | 旧版本保留 | 启动新版本 | B |
下次更新反向使用 A。构建失败不会覆盖运行中的槽,也不会修改 active_slot。
边界
@机器人 /update [-f] 与 WebUI 更新共用 HostLifecycle:获取 git 更新、构建备用槽、切换活动槽并请求 103 重启。-f 传给更新逻辑,即使未拉到新提交也继续构建和切槽。