机场导航

自建节点在线监控探针:从解析订阅到状态页的完整实现

更新时间:2026-10-16

本站 节点状态页 从一台 2GB 内存的廉价 VPS 上,每 10 分钟探测数百个节点的连通性,并维持 30 天历史、邮件告警。整套系统不到 300 行 Python,这篇完整拆解实现思路,你可以照抄到自己的服务器上。

总体架构

cron (每10分钟)
   │
   ├─ 1. 拉取订阅(每6小时一次,平时用缓存)──► node_cache.json
   │
   ├─ 2. 解析节点列表 ──► 过滤信息节点、按 host:port 去重
   │
   ├─ 3. 并发 TCP ping(2次取优,超时5s)
   │
   ├─ 4. 写入 SQLite ──► 保留30天
   │
   └─ 5. 评估告警(订阅失效/在线率<10%)──► Resend 发邮件
                    │
HTTP 服务 (tracker) ◄── 查询 SQLite 渲染状态页

设计原则:标准库优先。只用 Python 内置的 socket / sqlite3 / http.server,零第三方依赖,2GB 小机器无压力。

第一步:解析订阅

订阅接口按客户端 UA 返回不同格式(实测 v2rayN UA 稳定拿到 base64 分享链接)。base64 解码后是逐行 URI:

vmess://eyJ2IjoiMiIsInBzIjoiSEswMS...     ← 二次 base64 JSON
anytls://uuid@host:port?sni=xxx#节点名
trojan://password@host:port#节点名

解析要点:

  1. vmess 的 base64 里是 JSON,取 add / port / ps(节点名)
  2. 其他协议走标准 URI 解析,节点名在 # 后的 fragment 里(URL 编码需解码)
  3. 过滤信息节点:订阅商喜欢塞"剩余流量、套餐到期、TG 群组"这类伪节点,按关键词过滤,否则状态页全是噪声
  4. 去重:同一个 host:port 只探测一次(很多名字不同、实际是同一台机器)

第二步:并发 TCP ping

def tcp_ping(host, port):
    best = None
    for _ in range(2):            # 测两次取最优,过滤偶发抖动
        t0 = time.monotonic()
        try:
            with socket.create_connection((host, port), timeout=5):
                ms = (time.monotonic() - t0) * 1000
                best = ms if best is None else min(best, ms)
        except OSError:
            pass
    return best   # None = 不可达

几百个节点用 ThreadPoolExecutor(24) 并发,3 秒左右全部测完。注意:ping 目标节点是合规的探测流量,但不要对订阅接口高频请求——我们把它降到每 6 小时一次,避免被机场风控误伤。

第三步:存储与清理

SQLite 完全够用。单表 probe_results(ts, airport, node, host, port, proto, ok, latency),按 (airport, ts) 建索引。30 天数据量约几十万行,查询毫秒级。定期 DELETE WHERE ts < now-30d。

第四步:状态页渲染

状态页是查询数据库动态生成的 HTML,关键设计:

第五步:邮件告警

接入 Resend API(curl POST 即可,注意 Python urllib 会被其 Cloudflare 风控拦,curl 不会)。告警状态机:

避坑记录(都是踩过的)

  1. 机场订阅接口识别 UA,返回 YAML 还是 base64 全看运气 → 固定 v2rayN/6.0
  2. 与监控服务共用一个 SQLite 会 database is locked → 加 busy_timeout
  3. 裸 IP + 自签证书的订阅 → 需要 insecure 选项跳过校验
  4. 历史数据里的信息节点要清理,否则在线率被拉低

这套系统支撑了本站全部状态数据。配合 监控方法论 阅读,你能做出比自己买的服务更可信的稳定性判断。