m-ui 怎么做到改用户不重启、不掉别人的连接
加一个用户不该让另外两百个人断线。m-ui 靠三级重载做到这一点 —— 按实际改了什么选最小的那一级 —— 再加上 sing-box 识别用户方式上的两个细节。
三级,从最小的开始
| 改了什么 | 发生什么 | 谁有感觉 |
|---|---|---|
| 用户:新增、删除、启停、凭据、线路分配 | ReloadUsers —— 从库里重建每个入站的用户列表,换进运行中的入站;重新应用按用户的限速与设备数 | 只有凭据被拿掉的那个用户(故意断的) |
| 上游:新增、编辑、删除 | ReloadUpstreams —— 渲染出站集合,加新的、换改过的、删多余的;路由规则按出站名引用,不用动。上游改名会改变规则的引用,这种情况自动升级为 ReloadAll | 走被替换出站的连接会重连;其它一切不动 |
| 证书:续期、覆盖同一对文件 | 不经过重载 —— sing-box 监听证书文件,变了自己换;只有证书路径变了才 ReloadAll | 没人 |
| 线路:协议、端口、TLS、传输、参数、部署 | ReloadAll —— 渲染整份配置,停掉 box,启动新的;起不来就把上一份再启动起来 | 这台服务器上的所有连接重连(几秒) |
面板根据改动本身选级别,管理员从不需要判断"现在应用安全吗"。副机应用推送来的快照也用同一套逻辑:只有用户变化的快照,在每台副机上只触发第一级。
sing-box 怎么识别一个用户
在 sing-box 的入站里,用户就是一个凭据列表。每一项上的名字只是给日志和流量归属用的标签,真正匹配连接的是凭据。两个推论决定了 m-ui 的设计:
- 同名、不同凭据的两项,对 sing-box 来说是进同一个账户的两条路。m-ui 用它实现临时共享:共享地址拿到的是挂在本人名下的第二套凭据,借用者的流量和设备都算本人的,取消共享只拿掉那一套凭据。
- 换上一张缺了某个凭据的用户表,只会让那个凭据以后连不上 —— 已经建立的连接还在。所以换表之后 m-ui 会明确地关掉被停用、被撤销凭据的用户的连接,主机和每台副机都做(副机随快照收到被撤销的共享列表)。hysteria2 / TUIC / AnyTLS 还有一层:它们只在会话建立时鉴权一次,之后在同一条会话里开多少子连接都不再验;只断子连接的话客户端马上再开一条。所以 m-ui 给这三种入站的会话做了登记,换表时把被移除、换了凭据的用户的整条会话关掉(表没变的入站一条会话都不动),踢下线也是同一套。
"换用户表"的成本
重建一个用户列表是一次数据库读取加一次入站用户管理器的调用。几百个用户是毫秒级,而且发生在面板进程内 —— 没有配置文件、没有信号、也没有和守护进程重启内核的竞争。
为什么改上游也不重启
sing-box 的出站管理器可以在运行时增删出站。m-ui 把渲染出的出站集合和运行中的(按标签和渲染后的参数)做对比,只应用差异,再更新线路到出口的路由规则。把一条线路从直连改成走上游,或者换掉上游的凭据,都不会碰入站。
什么时候躲不开重启
监听器没法原地换协议或端口,所以改线路要重启数据面。两道保险让它不再危险:
- 新配置在保存事务里已经被 sing-box 解析过;解析不通过,保存就失败了,什么都没重启。
- 解析通过却起不来 —— 典型的是端口在这期间被别的进程占了 —— 上一份配置会被重新启动,面板显示错误。服务器不会处在没有数据面的状态。
和面板、其它线路、其它进程的端口冲突也在保存时就被拦下,所以重启这条路很少真的需要回滚。
相关:为什么内嵌内核 · 副机怎么收到同样的改动 · GitHub 上的 runner/