m-uising-box panel

深入 · 2026-09-29

m-ui 的配置校验与回滚是怎么做的

面板能做的最糟的事,是接受一次让代理整个挂掉的改动。m-ui 在"点一下"和"sing-box 跑起来"之间放了四道关,最后一道在前面全错的时候把上一份配置放回去。

第一道:字段检查

API 先检查表单发来的东西:协议是已知的、端口在范围内、JSON 字段能解析、名字没被占、TLS 模式和协议匹配、选了 Reality 就得有密钥、端口跳跃范围规范化。这些便宜、报错也最清楚,所以最先跑。

第二道:端口探测

端口冲突是 sing-box 解析时看不到的那种错误 —— 只有监听器绑定时才会失败。m-ui 把端口和其它线路、面板、订阅、代理面板的端口对照,然后真的绑一次(TCP 和 UDP)来抓机器上别的程序。没改端口的编辑跳过绑定,因为占着它的正是运行中的数据面。把面板挪到新端口时同样的规则反向适用。

第三道:事务里的 sing-box 解析器

有了这一道,前面两道才可以不完美。改动写在 SQLite 事务里;提交之前,m-ui 从数据库渲染出这台服务器的完整配置 —— 每条线路、每个上游、每个用户 —— 交给 sing-box 的选项解析器(core/validate.go)。解析器拒绝,事务就回滚,请求带着 sing-box 自己的错误信息失败。什么都没进运行中的内核,数据库里仍是上一份好状态。

因为渲染的是整份配置,这一道还能抓组合问题:两个上游会生成同一个出站标签、某个用户的凭据不适合新加的协议、某个选项只在和另一个选项同时出现时才非法。

第四道:起不来就回滚

配置可能解析通过却起不来:探测和重启之间端口被别的进程抢了、证书文件没了、内核拒绝某个 socket 选项。改线路会重启数据面,所以疼的就是这里。m-ui 在内存里留着上一份已应用的配置;新的起不来,就把旧的再启动,重新应用按用户的限制和端口跳跃规则,把错误报回面板。你修原因的时候,用户仍然在旧配置上有服务。

仍然抓不住的

副机

副机从主机收到完整快照,用同样的逻辑应用:解析、按级别应用、重启失败就回滚。应用不了快照的副机把错误报回去,保留上一份配置,下次推送再试 —— 主机从不让副机停在应用了一半的状态。

顺序为什么重要

便宜的检查先跑,报错更明白;昂贵的整份渲染只在便宜的通过后才跑;回滚是为静态检查看不到的情况准备的。每一道都允许不完美,因为下一道在那里。