m-ui 怎么用一台主机把很多台服务器同步好
服务器之间没有消息队列,也没有共享数据库。真相在主机的 SQLite 文件里,主机每五秒推一份完整快照,只拉回副机比它更清楚的两样东西:流量,和谁在线。
快照
每一轮,主机的 hub/ 读出线路、上游、用户(带凭据,去掉主机自己的用量计数)、用户↔线路分配、外部节点、代理,以及影响渲染的那几项设置,序列化后做一次 SHA-256,这就是修订号。快照故意做成完整的而不是增量的:无论副机漏掉了什么,一条消息总能把它带到正确状态。
推送,以及副机为什么是幂等的
副机上次确认的修订号和当前不同时,或者不管怎样每十分钟一次,主机把快照推过去。副机拿修订号和自己已应用的比:一样就立刻应答、什么都不做;不一样就在一个事务里把快照写进自己的库,再按和主机相同的三级重载应用 —— 只有用户差异就换入站用户表,上游差异换出站,线路差异重启数据面并带回滚。因为每次到的都是全量状态,离线一小时的副机只需要一次推送,不需要回放。
并发:一台慢的不能拖住其它的
从 v0.4.6 起,每台副机的"推送 → 拉报告"往返在自己的 goroutine 里跑。只有后面的数据库写入 —— 并入计数器、记录公网 IP —— 在主机上串行做,因为 SQLite 喜欢单写者。一台用满 25 秒超时的机器只耽误它自己 25 秒;其它机器照常几百毫秒完成。
流量:计数器与游标
副机从不做减法。每台副机为每个用户维护一个单调递增的转发字节计数,每 10 秒从内嵌内核更新一次。主机为每台副机、每个用户存一个游标:已经记过账的计数值。每份报告贡献的是 计数 − 游标,按该机的流量倍率加到用户用量和时序里,然后游标前移。
如果计数回来时比游标小,说明副机重装过或库被重置。主机把游标重新对齐到新值而不是做减法,所以重置永远不会产生负数或翻倍的用量。重复的报告(请求重试)也因此无害:增量是零。
跨服务器的设备数
设备数按不同源 IP 算,而 IP 是被客户端连上的那台服务器看到的。每份报告带着该副机上每个用户的在线 IP。主机把它们和自己的合并,同一轮里再把"在别处看到的 IP"发给每台副机。每台服务器于是对着并集执行限制:三台设备就是整个集群三台。执行仍在本机、仍然快;只有 IP 列表在传。
回传的还有什么
除了计数和 IP,报告还带副机的版本、内核是否在跑、运行时长、证书剩余天数、探测到的公网 IP(没手动填地址时作为订阅里的服务器地址),以及一小段最近连接给概览页看。关于主机的信息,除了快照,什么都不发给副机。
故障
- 副机不可达:拿着最后一份快照继续转发。主机标记离线,配置了 Telegram 的话一分钟后告警,每轮重试。回来后:一次推送,游标接上,不丢也不重复。
- 主机不可达:副机原样继续。配额执行暂停到主机回来为止,因为只有主机判配额 —— 刻意如此,网络分区永远不会误停用户。
- 删掉副机:只部署在它上面的线路会被停用而不是悄悄铺到所有服务器,它的游标和缓存被清掉。
通道的安全
每台副机有自己的令牌;主机保存它,通过 HTTPS 以请求头的形式发到副机的面板地址。副机只在副机模式下、只对正确令牌接受 agent 调用,比较用恒定时间。快照必然包含用户凭据 —— 副机的入站就需要它们 —— 所以副机面板应当放在 TLS 之后,自签的也行。
相关:多服务器总览 · 重载分级是怎么做的 · GitHub 上的 hub/