mihomo 节点拨不通排查 (Mihomo Node Connection Failure Diagnosis)
触发场景
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,这是大陆服务器直连被墙的必然结果,不是节点问题。
- 若 PROXY 组选中
节点层报错:
failed to create session: read tcp ... -> <node_ip>:<port>: read: connection reset by peer- TCP 能连、握手被 RST,特征指向服务商拒绝该客户端(订阅过期/限设备/封 IP/节点被拔线)。
分层排查步骤
- 服务与端口:
systemctl status mihomo;ss -tlnp | grep -E '7890|9090'。确认进程、监听是否正常。 - 订阅新鲜度:
systemctl status mihomo-subscription-update,看最近一次 update 是否成功;ls -la配置目录看 config.yaml 修改时间。 - 配置与节点清单:解析 config.yaml,列出 proxies 的 server/port(隐藏 password/uuid 等密钥)。注意配置可能把节点硬编码进 proxies(非 proxy-provider),订阅脚本负责改写 config.yaml。
- 节点服务器连通性:DNS
getent ahosts <domain>;TCP 用timeout 5 bash -c 'exec 3<>/dev/tcp/<host>/<port>'逐端口测。分清:解析失败 / TCP 不通 / 通了但握手被 RST。 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;本机(住宅宽带)同一订阅同一节点跑得通。诊断确认流程:
- 本机真客户端(mihomo)能跑 = 节点没坏,排除服务商故障。
- 服务器上正确 AnyTLS 客户端也被 RST = 只剩源 IP 差异。
curl ipinfo.io查出口 IP,org 显示AS45090 Tencent→ 机房 IP。- 结论:服务商封了腾讯云/机房 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 服务器、根本没发起对节点的连接。
- 先分清解析链再怪节点:用
curl "https://<公共DNS>/resolve?name=<节点域名>&type=A"(DoH,走 443)测节点域名解析——通了说明域名没问题。 剥洋葱:直连 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 层。
- 复现确认是远端: 绕过 Verge 全家桶,写一条只含单节点的最小 mihomo 配置独立跑
verge-mihomo.exe -d <dir> -f <mini.yaml>,日志直接给出真错误。见"分层排查步骤"里的纯净内核法。 - 查内核 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)。 - 之所以之前没事:旧 DNS 缓存还热着;某次把缓存清空(如 DNS 服务重启/客户端重启)后立刻露馅。"某天突然全挂、之前好好的"也可能是解析链上游被墙,而非机场死了。
- 修复:
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。
- 看客户端日志里实际拨的 IP:mihomo 日志 dial 行显示的是已解析的 IP 而非域名(
dial tcp 117.55.193.146:40001)。把日志里出现的 IP 收集起来,反查它来自哪个 DNS。 - 对同一节点域名用多个公共 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 分发)。 - 确认哪条线路死:对每个返回的 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 拉黑而非下线;联系服务商确认入口是否在轮换。 - 为什么客户端会踩中:mihomo 的
proxy-server-nameserver(解析节点域名的专用 DNS 列表)或 GUI 的 DoH 列表里含阿里(223.5.5.5 / dns.alidns.com)。阿里返回死 IP,客户端就用死 IP 拨号。两个客户端同源故障,因为解析链重叠。 - "换腾讯 DNS"是否有效先实测再信:本案例中腾讯对 h0/h1 新主机返回健康 IP,但对旧主机 h2 也返回死 IP —— 客服建议只对新主机成立。以实测为准。
- 恢复:改客户端 DNS(去掉返回死 IP 的那个,或换成 114.114.114.114 / 系统)+ 重启客户端清缓存;根因要服务商修 A 记录。诊断期间服务商把 h0/h1 的阿里记录改成了健康 IP,客户端重启即恢复。
恢复步骤
- 到服务商后台查订阅状态(过期、限设备数、封 IP)。
- 更新或更换订阅;
update-mihomo-subscription成功返回不代表节点有效——它只是把失效节点写进配置。 - 节点恢复后切组:
PUT /proxies/PROXYbody{"name": "<节点或组名>"};或直接选自动选择(URLTest)。
下次如何识别
- 先看日志是
i/o timeout(大概率 DIRECT/被墙)还是connection reset by peer(节点被服务商拒)。 - 服务正常 + 订阅新鲜 + 全部节点 RST ≈ 服务商侧问题,先查订阅账号。
- 全部节点超时但日志 dial 的是同一个陌生 IP:反查该 IP 来自哪个 DNS,对节点域名做多 DNS 解析对比——不同 DNS 给不同 IP 即智能 DNS 线路分裂,死线路大概率在客户端解析链里的某个 DNS 上。
相关概念
- Clash 380GB 日志文件问题 (Clash 380GB Log File Issue)(同属 Clash Meta 生态)
- 双 mihomo 内核切节点不生效 (Dual Mihomo Cores Silent No-Op) —— GUI 切节点无效的另一形态:状态分裂而非节点故障
- GFW 直连超时 vs 服务商拒连的区分
本文归档于个人知识库 · 领域:CS & AI · 模块:04 Project_CS_AI/Network