Clash API无人值守切换代理节点:Mihomo外部控制器进阶配置指南

为什么用 Clash API 无人值守切换代理节点? 一文看懂

这篇文章聚焦第二种用法,演示如何安全启用 external-controller、设置连通密钥、查询代理组,以及用 curl 和 Python 切换节点。不同客户端的设置菜单和配置文件管理方式也许略有差异,但只要跑起来的是兼容该 API 的 Mihomo 内核,核心请求方式基本一致。

平时在 Clash Verge、Mihomo Party 或其他 Mihomo 客户端里切节点,开启代理页面、找到策略组再点选节点,手工操作并不复杂。但当你得定时切换出口、在脚本里根据任务挑选节点,或在多台设备上执行相同操作时,反复点操作操作操作界面就不够弹性了。

此时能够运用 Mihomo 的外部控制器 API:客户端操作操作操作界面与 API 都在控制同一个 Mihomo 内核,脚本能够查询代理组、读取当前挑选,再向指定策略组提交切换请求。

先看这一点:得先区分两种「无人值守切换」。一种是 Mihomo 配置中的 url-test、fallback 或 load-balance 等策略组,由内核按照测速结果或可用状态选节点;

另一种是通过 API 按照你亲手的条件挑选节点,比如任务开始时固定到某节点、任务结束后恢复原挑选,或者由外部监控程序在故障时切换。API 本身不会替你决定哪条线路最好,它提供的是可编排的控制入口;挑选逻辑、切换频率和不成功后的处理,仍需由配置与脚本共同定义。

先确认控制对象:外部控制器连接的是 Mihomo 内核,不是某个客户端的网页操作操作操作界面。若你在配置里改了控制器端口或密钥,需确认客户端实际加载了这份配置,并在内核重载后再测试。没有启用 Mihomo 的旧版 Clash 核心,不能据此假设兼容相同接口。

启用 external-controller:先限制监听范围 核心要点

安全上,127.0.0.1 与 0.0.0.0 的差别十分关键:后者一般意味着监听全部网络接口。除非你明确得从另一台设备连通,并已通过防火墙、可信网络和强密钥限制来源,否则不要把控制器改成对外监听。不要把密钥放进公开仓库、截图、共享日志或命令历史;如果得在多台设备跑起来脚本,可运用环境变量或本机权限受控的密钥文件,而不是把密钥硬编码在代码中。

external-controller: 127.0.0.1:9090
secret: "请替换为足够长且随机的密钥"

外部控制器由配置项 external-controller 指定监听地址与端口,高频本机配置是 127.0.0.1:9090。这个地址只允许本机连通,适合个人电脑上的无人值守化脚本。推荐同时设置非空的 secret:控制器 API 能查询连接、调整策略组,甚至影响整台设备的代理出口,所以不应把无鉴权的控制接口暴露到局域网或公网。

补充说明:如果客户端通过图形操作操作操作界面管理配置,不要急着干脆改订阅原文件。先查看客户端的配置编辑、覆写或内核设置入口,再确认最终生效配置中包含控制器设置。部分客户端会在刷新订阅时重新生成配置,干脆编辑临时文件也许导致改动被覆盖。配置保存后,按客户端提供的方式重载配置或重启内核;只关掉设置窗口并不一定会使新端口立即生效。

不要将控制器端口当作代理端口:external-controller 是管理 API 的入口,不是 HTTP、SOCKS 或混合代理端口。把浏览器代理指向 9090 一般不会得到正常代理服务;反过来,也不要因为本机代理端口可连接,就推断控制器已经无误启用。

先查询代理组,再确认可选节点 逐条拆解

推荐先在输出中找到准确的组名和目标节点名,再执行切换。不要假设订阅刷新后名称永远不变:服务商也许调整节点命名,配置覆写也也许重命名策略组。无人值守化脚本若依赖固定名称,应在每次调用时验证目标是否仍在 all 列表中,而不是收到异常后继续发送盲目请求。

切换前先通过 GET /proxies 查看 Mihomo 当前识别到的代理与策略组。接口返回 JSON,顶层的 proxies 对象包含名称、类型、当前挑选以及可条目等信息。高频代理组名称涵盖 节点选择、Proxy 或机场自定义名称,但名称取决于配置,不能照搬示例。

curl --fail --silent --show-error \
  -H "Authorization: Bearer 请替换为你的密钥" \
  http://127.0.0.1:9090/proxies

组的 type 也值得核对:Selector 通常允许手工指定条目;URLTest 等无人值守测速组一般由内核按自身逻辑挑选,手工切换两者的结果不应与切换手工挑选组混为一谈。

  • 请求返回 401:通常表示没有提供有效密钥,核对请求头中的 Authorization: Bearer ... 是否与当前 Mihomo 配置一致。
  • 请求返回 404:先核对端口、请求路径和组名是否准确;组名大小写、空格以及 URL 编码都也许影响定位。
  • 连接被拒绝或超时:确认内核正在跑起来、监听端口与配置一致,并核对客户端是否把控制器绑定到了另一个地址。
  • 能查到组但看不到预期节点:核对该节点是否实际载入,以及它是否属于你查询的那一层代理组。

换个角度:如果只想查看某个组,可请求 GET /proxies/{组名}。组名中若有空格、斜线或其他特殊字符,应进行 URL 编码;在终端中手工拼接路径时尤其容易把请求发到失误地址。响应里要点看 type、now 和 all:now 是目前挑选,all 列出这个组可选的项目。某些项目也许是另一个策略组,而不是具体服务器节点,所以切换前要根据返回素材确认你选的是想要的层级。

用 curl 切换节点并验证结果 底层逻辑

如果策略组名称包含空格,构造接口路径时得编码,比如空格可表示为 %20。不同命令行环境对引号与百分号的处理也许不同;用变量拼接 URL 时,也应避免未编码的用户输入干脆进入路径。对于定时任务,最好将组名和节点名作为明确配置项保存,并在跑起来前核对两者仍存在,减少订阅变更后误选或请求不成功的状态。

curl --fail --silent --show-error \
  -X PUT \
  -H "Authorization: Bearer 请替换为你的密钥" \
  -H "Content-Type: application/json" \
  -d '{"name":"目标节点名称"}' \
  "http://127.0.0.1:9090/proxies/策略组名称"

确认策略组为可手工挑选的类型,并且目标节点出现在其可选列表中后,可运用 PUT /proxies/{组名} 刷新挑选。请求体是 JSON,键名为 name,值为要切换到的条目名称。下面的例子运用占位组名和节点名;请替换为 /proxies 响应里的真实值。路径中的组名应按 URL 规则编码,避免特殊字符造成接口找不到资源。

切换请求成功后,不要只凭 HTTP 请求没有抛出失误就判断任务完成。再次查询对应代理组,确认 now 已变为目标节点;随后还能够发起一次实际网络请求,并在客户端连接列表或日志里核对请求所用的出站。API 返回成功只能说明控制器接受了挑选请求,并不保证目标节点当前可用、远端服务能连通,或应用流量确实经过了预期策略组。

动手操作:用 Python 查询并切换策略组 一文看懂

若脚本由定时器或无人值守化平台跑起来,需确认它与 Mihomo 在同一网络命名空间内,并且跑起来时能够连通 127.0.0.1:9090。容器中的 127.0.0.1 指向容器自身,而不一定是宿主机;远程服务器上的计划任务也不会无人值守连接你个人电脑的本地控制器。对于这类落地,应先设计受限的网络连通方式,再配置控制器地址,不能容易地把监听地址改成公网地址。

import json
import os
from urllib.error import HTTPError, URLError
from urllib.parse import quote
from urllib.request import Request, urlopen

BASE = "http://127.0.0.1:9090"
GROUP = "节点选择"
TARGET = "目标节点名称"
SECRET = os.environ["MIHOMO_SECRET"]

def api(method, path, body=None):
    data = None if body is None else json.dumps(body).encode("utf-8")
    request = Request(
        BASE + path,
        data=data,
        method=method,
        headers={
            "Authorization": f"Bearer {SECRET}",
            "Content-Type": "application/json",
        },
    )
    with urlopen(request, timeout=5) as response:
        content = response.read()
        return json.loads(content) if content else None

group_path = "/proxies/" + quote(GROUP, safe="")
try:
    current = api("GET", group_path)
    options = current.get("all", [])

    if TARGET not in options:
        raise ValueError(f"目标节点不在策略组选项中: {TARGET}")

    if current.get("now") == TARGET:
        print(f"当前已使用目标节点: {TARGET}")
    else:
        api("PUT", group_path, {"name": TARGET})
        updated = api("GET", group_path)
        if updated.get("now") != TARGET:
            raise RuntimeError("切换请求已提交,但当前选择未变更")
        print(f"已切换到: {TARGET}")

except HTTPError as exc:
    print(f"API 返回 HTTP 错误: {exc.code}")
except (URLError, TimeoutError) as exc:
    print(f"无法连接 Mihomo 控制器: {exc}")
except (KeyError, ValueError, RuntimeError) as exc:
    print(f"自动切换未完成: {exc}")

具体来说:下面的 Python 示例运用标准库 urllib,不得额外安装 HTTP 客户端。它先查询指定策略组,验证目标节点属于可条目,再发送切换请求,最后重新查询当前挑选。将密钥放在环境变量 MIHOMO_SECRET 中,避免写入脚这篇文章件;组名和节点名则按本机 API 返回值修改。

在 macOS 或 Linux 的终端中,能够在拉起脚本前设置环境变量,比如 export MIHOMO_SECRET='你的密钥';Windows PowerShell 可运用 $env:MIHOMO_SECRET='你的密钥'。不要把真实密钥粘贴到公开终端录屏或共享 CI 日志里。示例设置了五秒超时,目的是避免控制器无响应时任务永久挂起;生产脚本还可加入日志记录、有限次数重试和告警,但不推荐无间隔地连续重试。

无人值守化设计:别让测速与脚本互相抢控制权 核心要点

推荐为脚本加上幂等判断:如果当前节点已经是目标,就不再发送写入请求;如果目标不存在,就明确抛出失误并停止;如果 API 请求超时,不要立即假定切换不成功,因为内核也许已经处理了请求。此时先重新查询当前状态,再决定是否重试。还能够为每次操作记录时间、策略组、原挑选、目标挑选和结果,但日志里不要记录密钥,也不要记录也许泄露订阅凭据的齐全配置素材。

  • 按任务切换:开始任务前保存原来的 now,切到目标节点;任务结束后可挑选恢复原节点。恢复前要再次确认当前值,避免覆盖用户在任务期间主动做出的更改。
  • 按健康状态切换:先用独立的连通性核对判断故障,再切换到备用节点,并设置冷却时间。不要把一次短暂超时干脆当成节点永久失效。
  • 按时段切换:运用明确的时间表并记录每次变更。应考虑系统睡眠、时区和任务重叠,避免设备唤醒后多个过期任务一起执行。
  • 按应用分流:首位在规则与策略组层面规划应用走向;只有确实得外部流程主动改变当前挑选时,才用 API 进行全局节点切换。

API 无人值守切换最容易出难点的地方,一般不是请求格式,而是多个机制同时控制同一个策略组。比如,脚本刚把 Selector 切到指定节点,另一个任务又在片刻后切走;或者用户正在执行长时间下载时,定时任务按固定间隔轮换节点,导致连接中断。要让无人值守化可预测,先明确每个策略组的控制者:哪些由用户手工挑选,哪些由 url-test 无人值守挑选,哪些只允许特定脚本修改。

顺带一提:节点切换不等于已有连接会无缝迁移。多数正在传输的 TCP 会话仍也许因为出口变化而断开,长连接、下载任务和远程登录尤其显著。对这类业务,更稳妥的做法是只在任务开始前切换,或把不同用途放在独立策略组中,让普通流量不受无人值守任务影响。切换后应留出连接重建时间,并通过实际业务请求验证结果,而不是用连续高速切换来「测试稳健性」。

高频难点:鉴权不成功、端口不可达与切换无效 逐条拆解

为什么返回 401 或提示鉴权不成功?(一眼看懂)

确认控制器配置中的 secret 与脚本运用的密钥完全一致,核对是否多了空格、换行或引号字符,并确认请求头格式是 Authorization: Bearer 密钥。若刚改过配置,先确认 Mihomo 已加载新设置。不要通过移除鉴权来绕过难点;本机控制接口仍应设置密钥。

为什么 127.0.0.1:9090 连接不成功?(核心)

先核对 Mihomo 内核是否跑起来,以及控制器配置的端口是否确实为 9090。客户端有时会管理独立的内核配置,用户编辑的另一份 YAML 也许并未生效。若脚本跑起来在容器、虚拟机或远程主机中,127.0.0.1 指向脚本所在环境,而不是桌面客户端所在设备;应先厘清网络边界,再决定如何安全连通。

请求成功了,为什么节点看起来没有切换?(逐条)

重新查询组的 now 和 all,确认请求地址中的组名无误、目标名称与可条目完全一致,并核对该组是不是手工挑选类型。若它属于无人值守测速组,内核也许会按组的机制刷新结果;若有另一段脚本同时跑起来,也也许立即覆盖你的挑选。切换后还要核对客户端连接详情,确认实际请求命中了预期的出站。

API 可连通,但代理请求仍然不成功,怎么判断?(机制)

Clash 将 Mihomo、Clash 客户端配置与高频排障流程放在同一套中文资料中,便于按客户端和实际故障逐项核对;如果你希望先选好合适的客户端,再按这篇文章搭建无人值守切换流程,能够前往下载并从本机受限的控制器配置开始测试。

控制器 API 与代理转发是两条不同路径。API 能响应只代表本机管理端口可用,不代表节点可用、规则命中无误或应用已经走代理。查看 Mihomo 的连接记录,核对目标域名、规则命中项和出站名称;再用同一代理组发起容易请求。如果 API 已成功切换而业务仍不成功,应继续排查节点状态、策略组层级、规则和应用代理设置,而不是反复修改控制器端口。

与手工在不同客户端操作操作界面里逐个寻找策略组相比,干脆调用 API 更适合得查询状态、复核结果和纳入任务流程的状态;但它也要求你认真管理密钥、监听范围和并发控制,单纯依赖定时轮换或复制一条命令并不能处理节点质量难点。