在多節點環境中,單純將所有 Xray 出站列在設定檔裡,並不代表流量會自動避開故障節點。某個節點可能仍能建立 TCP 連線,卻在 TLS 交握、REALITY 驗證、DNS 解析或實際傳輸階段失敗;也可能只是延遲突然升高、封包遺失增加,導致使用者感覺「偶爾能用、偶爾斷線」。要建立可靠的自動切換流程,必須將健康檢查、節點選擇、故障摘除、恢復判斷與切換日誌分開設計。
本文以 Xray API 與外部控制程式的配合為主軸,說明如何限制 API 權限、規劃節點 JSON、設計延遲與可用性探測,再依連續失敗次數把節點暫時移出服務。文中的做法適合自行維護代理伺服器、訂閱轉換服務、旁路由或需要高可用出口的網路環境。Xray 本身負責處理連線與路由,健康判斷和故障轉移則由控制層依明確規則執行。
從 Xray 的本機 gRPC API 權限開始,建立多出站節點與健康檢查流程,並以連續失敗、恢復門檻、冷卻時間和日誌事件控制自動故障轉移,避免因單次延遲尖峰造成頻繁切換。
先理解 Xray API 與控制層的分工
Xray API 通常透過 gRPC 提供服務,常見監聽位址是本機 127.0.0.1:10085。API 入站本身不等於代理入站,使用者的瀏覽器流量不應直接連到這個連接埠。控制程式透過 API 讀取統計資料、查詢或調整出站與路由狀態,代理流量仍然依照一般的 SOCKS、HTTP、TUN 或透明代理入站進入核心。
比較安全的拓撲是:Xray 的 API 僅綁定回環位址,控制程式與 Xray 位於同一台主機或同一個受控容器網路,外部管理請求不直接暴露到公網。若必須跨主機管理,應先透過防火牆限制來源 IP,再使用受保護的管理網路或安全隧道。不要因為 API 使用 gRPC 就把 0.0.0.0:10085 直接開放到網際網路。
{
"api": {
"tag": "api",
"services": [
"HandlerService",
"LoggerService",
"StatsService",
"RoutingService"
]
},
"inbounds": [
{
"tag": "api",
"listen": "127.0.0.1",
"port": 10085,
"protocol": "dokodemo-door",
"settings": {
"address": "127.0.0.1"
}
}
]
}
上面的結構只展示 API 入站與服務註冊的概念,不能直接套用到所有 Xray 版本或既有設定。實際部署前,應使用目前核心支援的服務名稱與設定格式進行驗證。啟動前先執行類似 xray run -test -config /etc/xray/config.json 的設定測試,確認 JSON 語法、API 服務與入站標籤沒有衝突,再重新載入程序。
節點 JSON 與路由選擇器如何設計
自動切換的第一個前提,是每個節點都要有穩定且唯一的識別方式。不要只依賴顯示名稱,因為訂閱更新後可能出現同名節點。建議為每個出站保留固定的 tag,例如 proxy-hk-01、proxy-jp-01 和 proxy-sg-01。控制程式以 tag 作為狀態鍵,將最近一次探測時間、成功率、連續失敗次數與目前狀態保存到獨立資料檔。
{
"tag": "proxy-jp-01",
"protocol": "vless",
"settings": {
"vnext": [
{
"address": "jp.example.net",
"port": 443,
"users": [
{
"id": "UUID-REPLACE",
"encryption": "none",
"flow": "xtls-rprx-vision"
}
]
}
]
},
"streamSettings": {
"network": "tcp",
"security": "reality",
"realitySettings": {
"serverName": "www.example.net",
"fingerprint": "chrome",
"publicKey": "PUBLIC-KEY-REPLACE",
"shortId": "SHORT-ID-REPLACE"
}
}
}
這段設定只代表 VLESS 搭配 TCP 與 REALITY 的欄位關係。VMess、WebSocket、TLS 或其他傳輸方式需要使用相應欄位,不能將 flow、realitySettings 或 WebSocket 路徑任意混用。若節點來自訂閱,應先確認訂閱匯入後的完整參數,再決定是否由控制程式管理;不要只複製位址和連接埠,就假設已保留整個握手條件。
節點選擇可以採用一個固定的候選順序,也可以使用 Xray 路由中的 balancer 概念,讓多個出站同屬一個選擇群組。控制程式的責任不是每次請求都改寫整份設定,而是維持「可用節點集合」與「目前偏好節點」。當目前節點被判定故障時,才調整路由選擇或更新執行中設定,減少頻繁重啟核心造成的連線中斷。
推薦方案:探測與轉送分離
探測控制層
- 每 30 秒檢查候選節點
- 保存成功率與延遲
- 連續失敗 3 次才摘除
Xray 執行層
- 保留代理與直連出站
- 依路由使用活動節點
- 寫入切換與核心錯誤日誌
探測結果先轉換為節點狀態,再由控制層調整出口;不要讓單次測試結果直接觸發核心重啟。
延遲探測不能只看 TCP 是否連得上
健康檢查至少要分成三層。第一層是 DNS 與 TCP 可達性,確認節點網域能解析、目標連接埠能建立連線;第二層是協定握手,確認 UUID、TLS、SNI、REALITY 公鑰、傳輸路徑等參數確實正確;第三層是經過代理出站存取固定測試目標,確認核心不只是「連到伺服器」,而是能完成實際轉送。
- 解析檢查:確認節點網域在控制主機上能取得有效 IP,並記錄解析耗時。
- 連接埠檢查:測量 TCP 建立時間,但不將成功連線視為節點完全正常。
- 代理請求:透過指定出站存取固定 HTTPS 測試網址,檢查狀態碼與總耗時。
- 重複取樣:在 30 秒或 60 秒週期內收集多次結果,計算成功率與 P95 延遲。
固定測試目標應選擇回應內容小、地理位置穩定、狀態碼容易判讀的 HTTPS 網址。不要每次探測都下載大型檔案,也不要只使用單一熱門網站,否則網站自身的限流、CDN 調度或短暫維護會被誤判為節點故障。測試請求的逾時可先設定為 8 秒,連續失敗 3 次才標記為不健康;實際數值仍應依伺服器距離和線路特性調整。
| 檢查層級 | 可證明的事情 | 不能直接證明的事情 | 建議結果 |
|---|---|---|---|
| DNS 解析 | 網域能取得位址 | 代理握手一定成功 | 記錄解析耗時 |
| TCP 連接埠 | 目標入口可建立基礎連線 | UUID、TLS 或 flow 正確 | 設定 3 至 5 秒逾時 |
| 代理 HTTPS | 核心能透過該出站完成請求 | 所有網站都同樣快速 | 記錄狀態碼與總耗時 |
| 連續取樣 | 一段時間內的穩定程度 | 未來尖峰時段一定正常 | 計算成功率與 P95 |
結論:可用率比單次最低延遲更重要
兩個節點的平均延遲只差 20 毫秒時,優先選擇連續 10 次成功率較高者;節點若偶爾出現 8 秒逾時,再低的平均值也不適合作為主要出口。
用狀態機控制摘除與恢復
自動故障轉移最容易出錯的地方,是把健康檢查結果直接等同於節點狀態。更穩妥的做法是為每個節點建立狀態機,例如 healthy、suspect、quarantined 和 recovering。單次失敗只進入 suspect,不立即切換;連續達到門檻後才進入 quarantined,並從候選集合中暫時移除。
假設探測週期為 30 秒,可以設定「連續失敗 3 次摘除、連續成功 3 次恢復、摘除後至少等待 120 秒再試」。這代表一次短暫網路抖動不會馬上導致切換,而真正故障的節點也不會在幾秒後反覆加入和退出。恢復測試應使用與故障判斷相同的代理請求,不能只看到 TCP 連線恢復就直接標記為健康。
- healthy:最近 3 次探測成功,允許作為候選節點。
- suspect:出現 1 至 2 次連續失敗,保留候選資格但提高記錄層級。
- quarantined:連續失敗達到 3 次,停止新流量選擇並記錄摘除時間。
- recovering:冷卻時間結束後重新測試,連續成功 3 次才回到 healthy。
切換時還要處理既有連線。大多數情況下,新的請求可以改走新節點,但已建立的 TCP 連線不一定能無縫搬移。不要為了切換一個出口而強制終止所有核心程序,否則 DNS、串流、下載與其他正常連線會同時中斷。若必須重新載入設定,應先確認 API 或服務管理方式支援平滑更新,並保留上一份可用設定作為回滾版本。
節點狀態範例:
healthy success=10 fail=0
suspect success=4 fail=2
quarantined success=0 fail=3 cooldown=120s
recovering success=2 fail=0
切換條件:
active 節點進入 quarantined
→ 從候選集合移除
→ 依優先順序選擇下一個 healthy 節點
→ 透過 API 更新路由選擇
→ 寫入切換原因與新節點 tag
日誌判讀、權限限制與常見陷阱
健康檢查控制程式應產生結構化事件,至少包含時間、節點 tag、探測類型、延遲、狀態碼、連續失敗次數、舊出口與新出口。Xray 的錯誤日誌則用來補充核心內部原因,例如上游連線逾時、TLS 交握失敗、REALITY 參數不一致、DNS 解析失敗或本機連接埠已被占用。兩者要以時間戳對照,不能只看控制程式最後一句「切換成功」。
2026-08-22T10:15:00+08:00 node=proxy-jp-01 probe=https status=timeout fail=3
2026-08-22T10:15:00+08:00 event=quarantine node=proxy-jp-01 reason=consecutive_failure
2026-08-22T10:15:01+08:00 event=failover from=proxy-jp-01 to=proxy-sg-01
2026-08-22T10:15:01+08:00 event=route_update result=success
2026-08-22T10:17:03+08:00 node=proxy-jp-01 probe=https status=200 latency=642ms state=recovering
API 連接埠應該開放到公網嗎?
不應直接開放。先將 Xray API 綁定 127.0.0.1:10085,讓控制程式在同機呼叫;若必須遠端管理,至少以防火牆限制來源並放在受控管理網路中。
TCP 連線成功,為什麼還被判定故障?
TCP 成功只代表遠端連接埠可達。若 VLESS、VMess、TLS、REALITY 或 WebSocket 參數錯誤,後續代理請求仍會失敗,因此應加入完整 HTTPS 代理探測。
單次逾時就切換可以嗎?
不建議。使用連續 3 次失敗與至少 120 秒冷卻時間,能降低短暫封包遺失造成的誤切換;低延遲但不穩定的節點不應頻繁成為活動出口。
節點恢復後要立即加回候選清單嗎?
先進入 recovering,使用相同的代理測試連續成功 3 次,再恢復為 healthy。重新加入前也要確認核心日誌不再出現握手或解析錯誤。
最後,控制程式本身也要具備故障保護。API 呼叫逾時時,不要清空目前路由;更新失敗時,保留上一個已知可用的出口;所有設定改動先寫入暫存檔並通過 JSON 檢查,再替換正式檔案。訂閱更新、節點健康檢查與路由切換最好使用不同排程,避免三個程序同時修改同一份設定而產生競態。
一套可維護的 Xray 自動故障轉移流程,核心不是把切換速度推到最快,而是讓每個判斷都可重現:探測目標固定、失敗門檻明確、摘除期間可追蹤、恢復條件對稱、API 僅供受控程序使用,並且所有變更都能在日誌中還原。完成初版後,應在低流量時段分別模擬 DNS 失敗、遠端連接埠關閉、TLS 參數錯誤與上游服務暫停,確認 Xray 仍保留直連路徑,控制程式也能在節點恢復後安全加入候選集合。