触发场景

mihomo 服务 systemd 运行正常、订阅显示刚更新成功,但经代理访问 Google/oauth2 等全部超时,日志刷 dial PROXY ... i/o timeout。

症状与报错特征

  • 服务日志:[TCP] dial PROXY (match Match/) ... oauth2.googleapis.com:443 error: dial tcp <ip>:443: i/o timeout

    • 若 PROXY 组选中 DIRECT,这是大陆服务器直连被墙的必然结果,不是节点问题。
  • 节点层报错:failed to create session: read tcp ... -> <node_ip>:<port>: read: connection reset by peer

    • TCP 能连、握手被 RST,特征指向服务商拒绝该客户端(订阅过期/限设备/封 IP/节点被拔线)。

分层排查步骤

  1. 服务与端口:systemctl status mihomo;ss -tlnp | grep -E '7890|9090'。确认进程、监听是否正常。
  2. 订阅新鲜度:systemctl status mihomo-subscription-update,看最近一次 update 是否成功;ls -la 配置目录看 config.yaml 修改时间。
  3. 配置与节点清单:解析 config.yaml,列出 proxies 的 server/port(隐藏 password/uuid 等密钥)。注意配置可能把节点硬编码进 proxies(非 proxy-provider),订阅脚本负责改写 config.yaml。
  4. 节点服务器连通性:DNS getent ahosts <domain>;TCP 用 timeout 5 bash -c 'exec 3<>/dev/tcp/<host>/<port>' 逐端口测。分清:解析失败 / TCP 不通 / 通了但握手被 RST。
  5. mihomo 内实测节点延迟:9090 API GET /proxies/<组>/delay?timeout=5000&url=...。

    • ⚠️ 节点名带空格和中文,必须先 URL 编码(用 Python urllib.parse.quote),否则请求静默失败返回空。
    • URLTest/故障转移组的 history 里 delay=0 = 健康检查失败。

根因判断

现象结论
订阅更新失败订阅源本身挂了
TCP 都不通节点 IP 被墙/下线/端口变更
TCP 通但握手 RST服务商拒连或节点故障,需进一步分辨(见下)
节点正常但 PROXY=DIRECT组选错,切换 自动选择 或可用节点即可

分辨"订阅被封"还是"服务商节点故障"

订阅链接抓下来先解码,区分公告行和封禁:

  • 订阅返回的 notice.invalid 行通常是公告/账户信息(剩余流量、重置日、到期日、官网),不是封禁标记,别误判为节点被禁。封禁会表现为:token 401 / 全部节点 notice.invalid 且无真实节点 / 剩余流量为 0。
  • 用本机 + 服务器两侧对照做 TLS 握手测试:两侧都失败 = 服务商节点故障;仅服务器失败 = 服务器 IP 被拉黑。
  • 查错误时间线:journalctl -u mihomo | grep -oE '^Aug [0-9]+' | sort | uniq -c。长期健康、某天突然 5 万+ 条 RST = 服务商当天故障;从装机起持续失败 = 账号/配置问题。
  • 订阅脚本通常会 PUT /proxies/PROXY {"name":"<首选节点>"} 自动切组,更新后节点失败时日志会出现大量 failed to create session。

实例:腾讯云服务器被封 ASN

症状:节点 TCP 通、AnyTLS 握手被 RST;本机(住宅宽带)同一订阅同一节点跑得通。诊断确认流程:

  1. 本机真客户端(mihomo)能跑 = 节点没坏,排除服务商故障。
  2. 服务器上正确 AnyTLS 客户端也被 RST = 只剩源 IP 差异。
  3. curl ipinfo.io 查出口 IP,org 显示 AS45090 Tencent → 机房 IP。
  4. 结论:服务商封了腾讯云/机房 ASN,防转卖。

注意:别用 curl 做普通 TLS 握手测试节点——AnyTLS 会 RST 掉非 AnyTLS 协议的任何连接,本机 curl 失败≠节点坏,只有真实客户端(mihomo)才是准的。

实例:nameserver 用国外 DoH 被封,代理"全节点超时"(Windows, 2026-09-02 SharkCloud)

症状:经代理 HTTPS 全失败(schannel: failed to receive handshake)、HTTP 返回 502;但直连、节点 TCP 端口、节点域名解析(用国内 DNS)全正常;内核日志干干净净,netstat 显示内核只连着两个 DoH 服务器、根本没发起对节点的连接。

  1. 先分清解析链再怪节点:用 curl "https://<公共DNS>/resolve?name=<节点域名>&type=A"(DoH,走 443)测节点域名解析——通了说明域名没问题。
  2. 剥洋葱:直连 vs 代理 vs 内核:

    • curl -x http://127.0.0.1:7890 https://google/generate_204 失败,-v 看是 CONNECT 成功但 schannel TLS 握手失败 → 隧道建立了,节点出去这层坏。
    • 再换成 curl -x ... http://www.gstatic.com 拿 502 → 502 是内核亲口说"我拨不出去",这是内核层判断,不是 TLS 层。
  3. 复现确认是远端: 绕过 Verge 全家桶,写一条只含单节点的最小 mihomo 配置独立跑 verge-mihomo.exe -d <dir> -f <mini.yaml>,日志直接给出真错误。见"分层排查步骤"里的纯净内核法。
  4. 查内核 DNS 上游是谁:读运行时配置 nameserver 项。本期是 https://dns.google/dns-query、cloudflare-dns.com、tls://dns.quad9.net——全部国外 DoH。这些 DoH 域名能解析(A 记录正常),但其 IP 被墙(/dev/tcp/8.8.8.8/443 直接 CLOSED)。
  5. 之所以之前没事:旧 DNS 缓存还热着;某次把缓存清空(如 DNS 服务重启/客户端重启)后立刻露馅。"某天突然全挂、之前好好的"也可能是解析链上游被墙,而非机场死了。
  6. 修复:nameserver 换成国内可直连 DoH(https://223.5.5.5/dns-query、https://120.53.53.53/dns-query),重启内核读取。DoH 走 443,TCP 端口可能被墙干扰,因此必须先实测每个 DoH 上游通不通再写进配置(用 curl https://<DoH>/dns-query?name=baidu.com&type=A -H 'accept: application/dns-json')。阿里那台这套 JSON 接口实测返回 no 'dns' query parameter found(接口格式不同)但服务器活着,不影响内核用标准 DoH 协议。

关键区分表(新增):

现象指向
netstat 内核只连着 DoH 服务器、没有到节点的 ESTABLISHED解析链/DNS 上游问题,不是节点
代理层 502(内核自己报)内核拨不出去(解析失败或节点死)
代理层 TLS 握手失败但 CONNECT 成功隧道建立,节点出去这层断
明文 connection reset by peer / forcibly closed by remote host,TCP 能连服务商/中间设备掐断,非本机

实例:智能 DNS 线路分裂,客户端全节点超时(Windows 双客户端)

症状:Clash Verge 与 Vortex 两个 mihomo 客户端全部 anytls 节点超时,ping 域名正常。排查发现不是节点全挂,而是解析链踩了死 IP。

  1. 看客户端日志里实际拨的 IP:mihomo 日志 dial 行显示的是已解析的 IP 而非域名(dial tcp 117.55.193.146:40001)。把日志里出现的 IP 收集起来,反查它来自哪个 DNS。
  2. 对同一节点域名用多个公共 DNS 分别解析对比:
    nslookup <node-domain> 223.5.5.5 / 119.29.29.29 / 114.114.114.114 / 8.8.8.8 / 本机系统 DNS。
    同一域名不同 DNS 返回不同 IP = 服务商智能/分线路 DNS(按解析器来源/EDNS Client Subnet 分发)。
  3. 确认哪条线路死:对每个返回的 IP 逐端口 timeout 5 bash -c 'echo > /dev/tcp/<ip>/<port>'。本案例:阿里 223.5.5.5 对 h1 返回的 117.55.193.146 全端口 REFUSED;而腾讯/114/谷歌/系统返回的 18.139.219.235 全开。→ 被拒连的 IP 不一定是退役节点,可能是服务商的真实国内入口被 GFW 阻断(连接 RST/超时)。判断依据:若同一批端口在更早时段可用、且该 IP 有部分端口半开,基本是被 GFW 拉黑而非下线;联系服务商确认入口是否在轮换。
  4. 为什么客户端会踩中:mihomo 的 proxy-server-nameserver(解析节点域名的专用 DNS 列表)或 GUI 的 DoH 列表里含阿里(223.5.5.5 / dns.alidns.com)。阿里返回死 IP,客户端就用死 IP 拨号。两个客户端同源故障,因为解析链重叠。
  5. "换腾讯 DNS"是否有效先实测再信:本案例中腾讯对 h0/h1 新主机返回健康 IP,但对旧主机 h2 也返回死 IP —— 客服建议只对新主机成立。以实测为准。
  6. 恢复:改客户端 DNS(去掉返回死 IP 的那个,或换成 114.114.114.114 / 系统)+ 重启客户端清缓存;根因要服务商修 A 记录。诊断期间服务商把 h0/h1 的阿里记录改成了健康 IP,客户端重启即恢复。

恢复步骤

  1. 到服务商后台查订阅状态(过期、限设备数、封 IP)。
  2. 更新或更换订阅;update-mihomo-subscription 成功返回不代表节点有效——它只是把失效节点写进配置。
  3. 节点恢复后切组:PUT /proxies/PROXY body {"name": "<节点或组名>"};或直接选 自动选择(URLTest)。

下次如何识别

  • 先看日志是 i/o timeout(大概率 DIRECT/被墙)还是 connection reset by peer(节点被服务商拒)。
  • 服务正常 + 订阅新鲜 + 全部节点 RST ≈ 服务商侧问题,先查订阅账号。
  • 全部节点超时但日志 dial 的是同一个陌生 IP:反查该 IP 来自哪个 DNS,对节点域名做多 DNS 解析对比——不同 DNS 给不同 IP 即智能 DNS 线路分裂,死线路大概率在客户端解析链里的某个 DNS 上。

相关概念


本文归档于个人知识库 · 领域:CS & AI · 模块:04 Project_CS_AI/Network