Files
MoShiro/docs/architecture.md
T
2026-10-04 21:46:39 +08:00

14 KiB
Raw Blame History

架构设计

1. 目标与定位

一个只关注网络层的线路质量面板:单体面板(Backend)+ 轻量节点(Probe、Agent)+ 长连接 RPC + 任务下发。核心能力:

  • 在一个家宽 Probe 与多台海外 VPS Agent(10 几台)之间,采集最原始的逐包、逐事件数据;
  • 由 Backend 的分析层推算:如果在某条线路上部署 REALITY 类、Hysteria2 类或 Minecraft 服务,体验会怎样,并对多条线路排序和对比;
  • 主机层信息只上报只读的汇总数字(2026-10 起,由用户确认放开):Agent 读 /proc 得到 CPU 占用、内存、根分区、网卡累计流量与瞬时网速、运行时长,不采集 CPU 型号、主机名、网卡名和任何 IP,不采集进程;Probe 不上报。展示页只能拿到这些数字。Agent 和 Probe 仍然是纯采集器,不具备 SSH、远程执行、终端、文件读写、远程更新等能力。

2. 拓扑

                         ┌────────────────── Backend(面板)───────────────────┐
  前端(待开发)◀─────────┤ Api(public|admin) │ 注册中心 │ 任务编排 │ 结果管线   │
    REST + SSE 实时      │ Analysis(指标注册表) │ LiveHub │ 存储(段文件+SQLite) │
                         └────▲──────▲──────▲───────────────▲─────────────────┘
               wss JSON-RPC   │      │      │               │   每个 Agent 一条长连接(由 Agent 发起)
                        ┌─────┴┐ ┌───┴──┐ ┌─┴────┐      ┌───┴──┐
                        │ HK-1 │ │ JP-1 │ │ SG-1 │ ...  │ US-3 │   10+ 台 VPS Agent(有公网 IP)
                        └──▲───┘ └──▲───┘ └──▲───┘      └──▲───┘
                           │        │        │             │     每个 Agent 一个会话:
                           └────────┴───┬────┴─────────────┘     TLS 控制通道 + UDP/TCP 数据通道
                                  ┌─────┴──────┐
                                  │   Probe    │   家宽,NAT/CGNAT 后,唯一的测量起点
                                  └────────────┘   不认识 Backend,只连 Agent
  • 线路(line) = 一对(Probe,Agent)。可以有多个 Probe(身份由 Backend 生成,可以吊销、删除),每个 Probe 连接多个 Agent,每个 Agent 同时服务多个 Probe;一条线路的数据按 line:<probe_id> 单独存放,互不混合。对外的线路 id 是 <agent_id>~<probe_id>。
  • 目标任务(target) = 某个 Agent → 指定的 IP:端口(ICMP、TCP/TLS 建连),只能访问 Agent 本地允许的网段。

3. 数据链路

 采集(只记录)                 传输(有界、至少一次)                分析(注册制、可重算)           展示
┌───────┐ 原始列(10s) ┌────────┐ RawChunk(10s)  ┌──────────────────────────────────────────┐
│ Probe │───────────>│ Agent  │──outbox(16)──>│ 段文件(30天) → Analysis → 指标窗口(1m)   │──> REST
│ 积木  │<══════════>│ 积木   │     wss        │   ↓ 流式处理未满窗口        ↓ 降采样 5m/1h │──> SSE
└───────┘  数据通道   └────────┘                │ LiveHub(内存快照)                       │
                                               └──────────────────────────────────────────┘
角色 职责 明确不做
Probe 连接所有启用的 Agent;执行线路任务中 Probe 一侧的角色;记录本端原始列并交给对应的 Agent 认识 Backend;统计;落盘测量数据;任何主机操作
Agent 注册;接受 Probe;执行 Agent 一侧的角色和目标任务;合并两侧的列并上传 统计;落盘测量数据;exec/终端/文件/自更新;采集主机信息
Backend 注册与身份;任务编排;持久化原始数据;分析;实时汇聚;API 连接 Probe

4. 关键设计(详见各专题文档)

主题 要点 文档
节点框架 注册、身份、连接、RPC、可靠上报、状态同步、宿主、更新都由与业务无关的 Fleet 提供;Monitor 只写业务,其他产品也能复用 fleet/overview.md
注册与身份 使用 fleet-enroll 和加入令牌自动注册,之后用签名认证;Probe 通过引导 Agent 同步 Agent 列表 enrollment.md
更新 独立的 fleet-updater 进程:只接受离线密钥签名的清单,更新策略由本地决定,失败自动回滚 fleet/update.md
任务模型 Task = {kind, target, params, schedule};持续采集、按需测试、目标任务统一建模;下发“期望任务集”,由节点自行调和 measurement.md
采集扩展 Kind 由积木组合声明;采集密度靠参数调整;原始块自描述 measurement.md
统计扩展 指标注册制:少数几种累加器 + 统计算子;一行 MetricDef 就是一个新指标 analysis.md
协议 控制链路使用 Fleet(JSON-RPC 2.0 + 二进制通道);Monitor 只定义 monitor.* 方法、TaskSet 状态文档和 UDP/TCP 数据面 protocol.md
存储 原始块追加写入段文件,SQLite 记录偏移量;指标窗口通用存储并降采样 storage.md
展示查询 任意时间段(相对或绝对);自动步长(每条曲线最多约 720 个点);1s 内存热层(1 小时)加上 10s/1m/5m/1h 各层;累加器现场合并,汇总精确;列式响应;SSE 实时跟随 query.md
API /api/public(只读,可设为公开)、/api/admin(登录)、/api/agent(注册与 RPC);实时数据用 SSE api.md

4.1 回显的四个时间戳

回显类 Kind 记录 t1..t4(Probe 发出、Agent 收到、Agent 回复、Probe 收到)。借此可以扣除 Agent 的处理耗时得到 RTT,可以分别判断上行和下行的丢包,并在去除时钟偏移和漂移后,得到每个方向各自的抖动,不需要两端时钟同步。

4.2 NAT / CGNAT 适配

所有连接和 UDP 流都由 Probe 发起;Agent 把回复发往请求的实际源地址。会话以 session_id 和 MAC 认证,不绑定五元组,NAT 映射变化后自动跟随新地址。持续的 UDP 回显本身就起到保活作用。

5. 安全边界:纯采集

Agent / Probe 能做的   执行编译在二进制中的 Kind;监听和访问本地配置允许的端口与网段;受本地硬上限约束
不能做的              exec / shell / 终端 / 文件读写(配置目录除外)/ 远程更新 / 加载外部代码 / 采集主机标识(型号、主机名、IP、进程)
即使 Backend 被攻破    攻击者最多能在本地硬上限内发测量流量,拿不到主机权限,也无法把 Agent 当成扫描器或放大器;
                      也推不出恶意更新(更新清单必须由离线密钥签名)
  • 本地硬上限写在节点本机的配置文件中,Backend 下发的任务如果超出,节点直接拒绝:允许访问的目标网段(默认为空,即不开放目标任务)、总码率和包速率上限、允许监听的端口。
  • 构建期强制:常驻节点进程使用 BannedSymbols.Node.txt,禁用 System.Diagnostics.Process、Assembly.Load*、NativeLibrary.Load、HttpClient,以及 ConfigStore 以外的文件写入 API,详见 project-layout.md 第 7 节。
  • 三进程权限分离:注册(fleet-enroll,一次性运行)、采集(monitor-agent / monitor-probe)、更新(fleet-updater)是三个独立进程,使用不同的系统用户。采集进程无法改写自己的二进制;更新进程读不到节点身份私钥。
  • 最小权限运行:非 root 用户;只在需要时授予 CAP_NET_RAW(ICMP);默认端口都高于 1024,不需要 CAP_NET_BIND_SERVICE,只有在 Agent 本地显式开启 443 时才授予。
  • 身份:节点使用本地生成的 ECDSA P-256 密钥对签名认证,Backend 只存公钥,数据库泄露也无法冒充节点。
  • 防放大:未认证的包静默丢弃;回复长度与请求相等;每个流按声明速率限速;使用重放窗口。
  • 自身状态:节点上报进程内的计数(会话数、包速率、队列、丢弃数);Agent 另外每隔几秒用 monitor.host 上报一份主机资源占用的汇总数字(可以在 agent.json 里用 reporting.host_stats = false 整个关掉),最新值实时推给展示页,Backend 每分钟存一个样本(默认保留 7 天,retention.host_days)。

6. 可扩展性目标

变更 需要改动的地方
调整采集密度(例如 TCP 20 Hz → 100 Hz,tcp_info 100 ms → 10 ms) 在 API 中修改任务参数,立即下发生效
新增采集方法(现有积木可以组合出来) 在 Kind 目录中加 1 条声明,然后更新 Probe/Agent 版本
新增采集方法(需要新积木) 1 个积木类 + 1 条 Kind 声明
新增统计指标 1 行 MetricDef(如需新算子,再加 1 个算子类);自动回填 30 天历史
调整服务评分 修改模型的评分函数;评分在查询时计算,立即对全部历史生效

Kind 目录编译在二进制中,节点不接受远程下发代码(安全边界)。节点在 fleet.hello 的 capabilities 中上报自己支持的 Kind,Backend 只下发它支持的类型。

7. 可靠性与资源

  • Probe:每个 Agent 未交付的原始列 ≤ 8 MB;Agent:上传队列 ≤ 64 MB,满时丢弃最旧的块,并通过 monitor.event(gap) 上报缺口。
  • 至少一次投递:Backend 持久化之后才回复 ACK,按 chunk_id 去重。
  • 节点重启会丢失未上传的数据,并体现为数据缺口。
  • 窗口时间以 Agent 时钟为准;时钟偏差超过 5 秒时记录告警事件。
  • 规模:10+ 个 Agent × 2~4 种持续 Kind × 每 10 秒一块,总计约每秒 2~6 块。

8. 设计决策记录

# 决策 理由
D1 只关注网络层;节点是纯采集器,没有远程执行能力 由用户确认;防止权限泄露
D2 拓扑为多个 Probe 连接多个 Agent(10+);线路 = (Probe, Agent),数据按 Probe 隔离 由用户确认
D3 Agent 用加入令牌自动注册,之后用密钥对签名认证 批量部署省事;Backend 不保存可用于冒充节点的秘密
D4 Probe 不认识 Backend,通过引导 Agent 同步 Agent 列表 新增 Agent 后自动覆盖,不需要改 Probe 配置
D5 只采集逐包、逐事件的原始数据,解释全部放在 Backend 模型和指标可以事后重算;采集端最轻
D6 统一的任务模型 + 期望任务集调和 新增任务类型时协议不变
D7 Kind 由积木组合声明;原始块自描述 调整密度或新增采集方法只改一处
D8 指标注册制,使用固定的少数几种累加器 新增统计只改一行;存储、降采样、API 全部通用
D9 JSON-RPC 2.0 信封 + 二进制帧 新增能力只需要加方法
D10 原始数据每 10 秒切一块,追加写入段文件,保留 30 天;指标长期保留 同一条链路同时服务实时和历史;避免海量小文件
D11 API 分为 public / admin / agent 三层;实时数据用 SSE 前端只读场景足够;反向代理和 CDN 更友好
D12 单仓库,三个独立项目加待开发的 Web;契约以 C# 代码为真源 改一处,编译期即可发现不一致
D13 Probe/Agent 运行时只依赖 BCL;.NET 10 NativeAOT 轻量
D14 节点机制抽成与业务无关的 Fleet 框架,放在同一仓库的 backend/fleet/ 目录,暂不独立(由用户确认;不设拆分时间表,需要时再评估;不使用 PublicApiAnalyzers) 可复用;samples/EchoNode 作为第二个使用者,防止过度设计
D15 注册独立为 fleet-enroll 工具;常驻进程不包含注册代码 常驻进程去掉 HttpClient,边界更纯
D16 更新独立为 fleet-updater:离线签名、多发布源、本地策略、健康确认与回滚(默认方案,待确认) 面板被攻破时也无法推送恶意二进制;可以同时管理多个产品
D17 Probe 支持 OpenWrt:正式支持 linux-musl-x64、linux-musl-arm64;armv7 视工具链验证结果决定;MIPS 不支持,改为在局域网设备上运行 Probe 由用户确认;NativeAOT 不支持 MIPS
D18 默认测量端口不使用 443:控制 7443/tcp,UDP 数据 7443/udp,TCP 数据 7444/tcp;443 只在 Agent 本地显式开启时才允许使用 由用户确认;避免与 VPS 上的真实服务冲突;结果会记录实际端口
D19 展示查询最细 1s:最近 1 小时来自内存热层,更早的数据来自 10s/1m/5m/1h 各层,现场合并;时间段内的汇总由累加器精确合并得出 由用户确认;5 分钟、15 分钟这类视图足够精细且快
D20 时序响应采用列式格式(t[] + values{},时间轴使用 Unix 毫秒) 由用户确认;体积小,可以直接交给图表库

9. 仍待确认

以下两项是按推荐方案写入的默认值(D16),等待确认:Fleet.Link 的通用范围(当前包含注册、认证、RPC、发件箱、状态同步、点对点会话);更新采用多发布源加离线签名。其余待确认事项:

  1. 告警:首版不包含,是否需要预留 Webhook?
  2. 与真实服务共用 443(例如 SNI 分流):首版不做,默认使用非 443 端口;以后如有需要再评估。