External Controller API 能做什麼:把手動切換變成可觀測流程
在 Clash 或 Mihomo 的日常使用中,external-controller 不只是給圖形介面讀取狀態的入口,也可以讓腳本查詢目前策略組、節點延遲、代理流量與連線資訊,再按照事先定義的條件自動切換。當你在 NAS、家用伺服器、CI 工作站或長時間執行的開發環境中使用代理時,單純依靠桌面客戶端手動點選節點並不可靠:人不一定在現場,節點可能在半夜失效,某些服務也可能因為出口 IP 或延遲突然變化而中斷。
這套方法的核心不是「每隔幾秒盲目換節點」,而是建立一個可追蹤的閉環:先從 API 取得策略組和代理的真實名稱,再用固定探針測試可用性,接著依照延遲、錯誤率、連續失敗次數與冷卻時間作出判斷,最後透過 API 更新策略組。如此一來,切換行為有明確理由,也能避免腳本在網路抖動時反覆跳線。對伺服器而言,這比在瀏覽器裡看一眼延遲數字後手動選擇更接近真正的運維流程。
需要先區分三個概念。第一是控制平面,也就是 API 本身,通常由 external-controller 指定監聽位址與連接埠;第二是策略組,例如 AUTO、PROXY 或自訂的 SERVER-POOL,它決定流量經過哪個出站;第三是實際代理節點,也就是策略組內可被選取的成員。腳本應該切換策略組,而不是直接修改整份 YAML,因為執行時的狀態由核心維護,直接改檔案往往要重新載入,還可能被訂閱更新覆蓋。
127.0.0.1 或可信任的內網位址,並設定密碼。不要把未授權的 API 直接暴露到公網;外部控制器等同於代理核心的管理介面,取得權限後可能讀取連線資訊、切換出站,甚至終止連線。
設定 external-controller、密碼與允許的 API 範圍
在設定檔中,常見的基本欄位包括 external-controller、secret 與部分 Mihomo 版本支援的控制器選項。最保守的本機配置可以像下面這樣,連接埠請依照你的環境調整,避免與 mixed-port、socks-port 或其他服務衝突。
external-controller: 127.0.0.1:9090
secret: "change-this-to-a-long-random-secret"
127.0.0.1:9090 只允許同一台機器上的程式連接,適合在 NAS 上以 cron、systemd timer 或容器內腳本呼叫。如果腳本位於另一台管理主機,才需要考慮綁定內網 IP;這時應搭配防火牆白名單、VPN 或 SSH 隧道,不建議直接使用 0.0.0.0:9090。即使你認為家中路由器沒有對外轉發,也不應把安全性建立在「應該沒人連得到」的假設上。
API 請求通常透過 Authorization: Bearer 傳送密碼。密碼不要硬編碼在公開 Git 儲存庫、Docker image 或 shell 歷史中;可放在只有服務帳戶可讀的環境檔,並限制檔案權限。若使用 systemd,建議透過獨立的 EnvironmentFile 注入;若使用 NAS 排程,則把秘密存放在權限受限的設定目錄,並避免在錯誤日誌中直接輸出完整請求標頭。
啟用後,先用最小權限方式確認控制器有回應。查詢版本與代理列表屬於低風險檢查,成功後再測試策略組切換。請注意不同核心版本對端點和回應欄位的支援可能不完全一致,Clash Verge、Clash Verge Rev、Mihomo Party 與直接執行 Mihomo 核心的行為也可能存在差異。不要只根據某個客戶端介面的名稱寫死腳本,應以目前核心實際回傳的 JSON 為準。
| 用途 | 常見 API 路徑 | 腳本應確認的內容 |
|---|---|---|
| 讀取核心資訊 | /version |
核心版本與回應格式 |
| 列出代理與策略組 | /proxies |
群組名稱、成員名稱、目前選擇 |
| 切換策略組 | /proxies/{group} |
以 JSON 傳送目標代理名稱 |
| 檢查連線狀態 | /connections |
目前連線數、主機與流量狀態 |
先設計策略組,再讓 API 負責選擇節點
自動切換是否穩定,首先取決於策略組設計。建議另外建立一個專用群組,例如 API-AUTO,不要直接操作整份配置中的主 PROXY 組。這樣可以讓自動化腳本只影響特定用途;瀏覽器、下載服務與開發工具若需要不同出口,也能分別建立策略組,避免 NAS 上的一個健康檢查把所有流量一起改掉。
策略組內的成員名稱必須精確匹配 /proxies 回傳的名稱,包含空格、符號與 emoji。訂閱更新後,節點名稱可能改變,這是自動切換腳本最常見的失效原因之一。實務上可在腳本啟動時檢查候選清單是否仍存在;若全部名稱都不存在,應記錄清楚的錯誤並停止切換,而不是把一個未知字串送給 API,然後誤以為切換成功。
節點選擇也不能只看單次延遲。某個節點可能對探針網址很快,卻在真正的 API、Git、套件鏡像或影音服務上頻繁重置。較可靠的做法是把探針分成兩層:第一層是短時間可完成的基本 HTTPS 請求,用來確認網路沒有完全中斷;第二層是與實際工作相關的網址,例如開發環境使用的 Git 主機或套件登錄站。若第二層需要登入或包含敏感資料,應選擇只回傳狀態碼的公開健康端點,不要把帳號資訊放進監控請求。
一般可以採用「候選節點、目前節點、備援節點」三段式邏輯。候選節點先按延遲排序,但只有連續兩次或三次通過檢查才可升級為新節點;目前節點只要仍在可接受門檻內,就不要因為其他節點快幾十毫秒而立刻切走;所有候選都失敗時,才切到明確的備援或保持原選擇。這種遲滯設計能降低頻繁切換造成的 TCP 重連、登入失效與工作中斷。
Python 控制腳本:查詢、測速與安全切換
下列示例使用 Python 標準函式庫,重點是展示 API 互動和判斷順序。正式使用時,請把控制器位址、密碼、策略組與探針網址改成自己的值。腳本不應假設回應永遠完整,因此對 HTTP 狀態碼、JSON 格式、群組不存在和空候選清單都加入處理。延遲測試使用核心提供的代理延遲端點時,實際路徑與參數可能因核心版本不同而有差異;若該端點不支援,可改用 Python 透過本地 mixed-port 發出獨立 HTTPS 請求。
import json
import os
import time
import urllib.request
import urllib.parse
CONTROLLER = os.environ["CLASH_CONTROLLER"].rstrip("/")
SECRET = os.environ["CLASH_SECRET"]
GROUP = os.environ.get("CLASH_GROUP", "API-AUTO")
CANDIDATES = ["Tokyo-01", "Singapore-02", "US-West-01"]
def request(path, method="GET", payload=None):
data = None
headers = {"Authorization": f"Bearer {SECRET}"}
if payload is not None:
data = json.dumps(payload).encode("utf-8")
headers["Content-Type"] = "application/json"
req = urllib.request.Request(
CONTROLLER + path, data=data, headers=headers, method=method
)
with urllib.request.urlopen(req, timeout=8) as response:
return response.status, json.loads(response.read().decode("utf-8"))
def choose_proxy():
_, all_proxies = request("/proxies")
group = all_proxies.get(GROUP)
if not group:
raise RuntimeError(f"strategy group not found: {GROUP}")
available = set(group.get("all", []))
current = group.get("now")
valid = [name for name in CANDIDATES if name in available]
if not valid:
raise RuntimeError("no configured candidate is available")
target = valid[0]
if target != current:
path = "/proxies/" + urllib.parse.quote(GROUP, safe="")
request(path, method="PUT", payload={"name": target})
print(f"switched {current!r} -> {target!r}")
else:
print(f"keep current proxy: {current!r}")
if __name__ == "__main__":
choose_proxy()
這段程式把「候選名稱存在」放在切換之前,但仍然是一個簡化版本。生產環境應在外層增加重試與冷卻時間,例如第一次請求失敗後等待數秒再試,連續失敗達到門檻才切換;切換完成後再重新查詢群組的 now 欄位確認核心是否接受。若 API 回傳錯誤,不能僅依靠腳本輸出的「已切換」文字判斷成功,因為網路中斷可能發生在請求送出或回應返回的中間階段。
另一個重要原則是不要讓多個排程程序同時修改同一個群組。可以使用檔案鎖、SQLite 狀態表或作業系統提供的單例機制,確保前一次測試尚未完成時,下一次執行會直接退出。狀態表至少記錄目前節點、最後一次成功測試時間、連續失敗次數、最後切換原因與腳本版本。這些資料對追查「為什麼凌晨自動換線」非常有用,也能幫你分辨節點故障、控制器故障和探針服務故障。
排程、告警與常見故障排查
在 Linux NAS 或伺服器上,短週期任務可以用 systemd timer 或 cron。若每分鐘檢查一次,腳本必須具備逾時控制,不能因為某次 DNS 或 HTTPS 卡住而累積大量程序。對需要更細緻重試的服務,systemd timer 通常比單純 cron 更容易指定失敗重試、日誌輸出與服務帳號;容器環境則可把腳本與秘密檔分離掛載,避免重新建立 image 時把密碼打包進去。
監控不應只發出「切換成功」通知。至少要觀察控制器是否可連線、策略組是否存在、候選節點數量、目前節點連續失敗次數、最後切換時間、探針狀態碼與總切換次數。當一小時內切換超過合理上限時,可能表示探針太嚴格、節點池整體不穩、DNS 解析異常,或腳本門檻設得過低。告警內容應包括群組名稱、原節點、新節點和原因,但不要把完整 token、訂閱 URL 或內部主機名稱直接發到公開聊天頻道。
- 401 或 403:檢查
secret、Bearer 標頭與控制器實際載入的設定檔,並確認客戶端重啟後沒有使用另一份配置。 - 404 或群組不存在:確認策略組名稱是否有空格、大小寫或 emoji 差異,也要確認腳本連到的是正確核心,而不是另一個客戶端的連接埠。
- 切換後流量仍走舊節點:檢查規則是否真的指向該策略組,並查看既有連線;已建立的長連線不一定會因為策略組切換而立刻重建。
- 延遲數字很低但服務仍失敗:更換探針為接近實際業務的 HTTPS 端點,並把 HTTP 狀態碼、TLS 錯誤和 DNS 失敗分開記錄。
- 節點列表突然變空:先確認訂閱更新、代理提供者與核心載入狀態,不要讓自動化程式在空列表時把流量切到不明的直連或拒絕組。
運維提醒:自動切換的目標是維持服務可用,不是追逐每一次最低延遲。若應用程式正在進行資料庫遷移、上傳大檔或長時間串流,切換出口可能中斷現有連線;可以在腳本中加入維護視窗、連線數門檻或手動暫停旗標,讓人工作業優先於自動策略。
相較於只提供圖形介面點選的傳統代理工具,Clash API 的優勢是能把節點狀態、策略組與排程監控接進既有的 NAS 或伺服器維運流程;但自行拼接 curl 指令容易漏掉授權、錯誤處理和切換冷卻,某些封閉客戶端也不一定提供一致的控制端點。Clash V.CORE 則更適合把這類需求集中管理:你可以保留熟悉的策略組與規則模型,再配合外部控制器、可讀日誌和多平台核心,逐步建立可驗證的自動選路流程;若你希望在桌面與伺服器之間共用同一套配置,現在就可以前往下載 Clash V.CORE,從受限的本機控制器與一個專用策略組開始實作。