Xray API 節點自動切換腳本:StatsService 進階設定指南

把 Xray 的流量監測與節點管理交給 API 和自動化腳本,即可建立更穩定的多節點代理架構。本文整理 StatsService、HandlerService 的實作方式,並提供 JSON 範例與生產環境的安全、回復及記錄策略。

Xray 的節點自動切換,不只是定時執行「測試網址」再更換設定檔。比較可靠的做法,是讓 Xray 透過 API 暴露流量統計與出站管理能力,由外部腳本讀取每個節點的上行、下行計數,再按照錯誤率、延遲、流量增長或連續失敗次數決定是否切換。這樣可以避免頻繁重啟核心,也能保留目前的路由規則、DNS 設定與入站連接。

本文以 Xray-core 1.8.x 以上版本的 API 結構為基準,重點說明 StatsService 與 HandlerService 的關係、API 監聽器的安全邊界、統計名稱的組成方式、節點切換腳本的設計,以及切換失敗時如何回復、記錄和限制頻率。範例會使用本機 127.0.0.1:10085 作為 API 入口;實際部署時應依目前設定檔與用戶端產生的連接埠調整。

本文速覽

本文適合已能使用 Xray、VLESS 或 VMess 節點,並希望以腳本自動選擇較穩定出站的使用者。內容從 API 設定、StatsService 計數規則與 HandlerService 切換方法開始,逐步建立一個具備門檻、冷卻時間、鎖定檔、回復節點與 JSON 記錄的生產環境方案。

Xray API 的服務分工與切換邏輯

Xray API 本身不是一個面向公網的管理網站,而是由 gRPC 提供服務介面。設定檔中的 api 區塊會指定一個 API 入站標籤,例如 apiapi.services 再決定此入站可以提供哪些服務。StatsService 負責查詢統計資料,HandlerService 則負責在核心執行期間管理入站或出站。兩者可以同時啟用,但用途完全不同。

應用程式發出請求 路由選擇出站 StatsService 累計 腳本計算健康度 HandlerService 切換

StatsService 回傳的是計數器,不是完整的即時品質報告。常見項目包括某個出站的 uplink 與 downlink 位元組數,也可能包含入站或使用者層級的統計。腳本需要在兩個時間點讀取同一個計數器,使用差值計算期間流量;不能把累計值直接當成最近一分鐘的傳輸量。若核心重啟,計數器可能歸零,腳本必須把這種情況視為重新建立基準,而不是誤判節點突然變得非常健康或非常異常。

HandlerService 的作用是改變目前核心中的處理器狀態。例如在多個出站都已經預先寫入設定檔時,腳本可以選擇某個出站作為目前使用的項目,而不必每次切換都重寫完整 JSON 或重新啟動 Xray。這種方式的中斷時間通常比重啟核心短,但能否使用指定方法,仍取決於 Xray-core 版本、API protobuf 定義與目前路由結構,部署前應先在測試環境驗證。

10085
建議的本機 API 連接埠範例
2 次
計算流量差值所需的讀取點
30 秒
建議的初始健康檢查週期
3 次
建議的連續失敗切換門檻

結論:統計負責觀察,切換仍要由策略決定

StatsService 只能告訴腳本計數器如何變化,不能直接證明某個節點適合所有流量。應將統計、主動探測、連續失敗次數與冷卻時間合併成決策,避免一次短暫延遲就觸發全域切換。

建立受限的 API 服務設定

最小可用的設定通常需要一個 API 入站、一個具備 API 標籤的本機入口,以及 StatsServiceHandlerService 服務。API 入站的位址應限制為 127.0.0.1,不要監聽 0.0.0.0 或伺服器的公網 IP。Xray API 一旦暴露到外部網路,取得連線能力的人可能修改出站、加入入站,甚至將服務變成未授權的轉發器。

{
  "api": {
    "tag": "api",
    "services": [
      "StatsService",
      "HandlerService"
    ]
  },
  "inbounds": [
    {
      "tag": "api",
      "listen": "127.0.0.1",
      "port": 10085,
      "protocol": "dokodemo-door",
      "settings": {
        "address": "127.0.0.1"
      }
    }
  ],
  "stats": {},
  "policy": {
    "levels": {
      "0": {
        "statsUserUplink": true,
        "statsUserDownlink": true
      }
    },
    "system": {
      "statsInboundUplink": true,
      "statsInboundDownlink": true,
      "statsOutboundUplink": true,
      "statsOutboundDownlink": true
    }
  }
}

上面的設定只展示與 API 和統計相關的部分,不是可以直接覆蓋現有設定檔的完整配置。policy.system.statsOutboundUplinkstatsOutboundDownlink 是觀察出站流量的關鍵;若沒有開啟,腳本即使能連上 StatsService,也可能查不到所需的出站計數器。若要統計特定使用者,還要在對應的 policy.levels 和用戶設定中啟用使用者統計,不能只開啟 system 統計。

API 入站本身通常不應被一般代理流量使用。路由規則要將 api 入站標籤送往 api 出站,或依 Xray 版本與配置方式使用內建 API 對應機制;重點是確保外部請求不能透過普通代理入口間接存取管理服務。修改後先用設定檔測試功能檢查,再重啟測試實例,最後才套用至正式程序。

StatsService 計數器名稱與 JSON 查詢

統計查詢最容易出錯的地方,是腳本使用了錯誤的名稱。Xray 統計資料通常以「類型 + 標籤」組成,例如出站 uplink 和 downlink 常見形式為 outbound>>>edge-a>>>traffic>>>uplinkoutbound>>>edge-a>>>traffic>>>downlink。這裡的 edge-a 必須與出站設定的 tag 完全一致,大小寫、連字號和底線都不能自行簡化。

可先使用 Xray 提供的 API 命令列工具查詢目前的統計項目。不同發行版本的命令列參數名稱可能略有差異,執行前可查看本機核心的說明;常見查詢形式如下:

xray api statsquery --server=127.0.0.1:10085
xray api stat query --server=127.0.0.1:10085 \
  -name "outbound>>>edge-a>>>traffic>>>uplink"

如果第一個命令可以列出統計項目,先把完整名稱保存到測試記錄,再讓腳本使用完全相同的名稱。查不到資料時,依序檢查出站標籤、system policy 是否開啟、流量是否真的經過該出站,以及 Xray 是否在修改設定後成功重新載入。不要直接把「回傳 0」解讀成節點沒有流量;有些情況是名稱不存在,有些情況才是計數器確實為零。

出站計數器

上行項目
outbound>>>edge-a>>>traffic>>>uplink
下行項目
outbound>>>edge-a>>>traffic>>>downlink
識別依據
出站 tag
資料型態
累計位元組數

以兩次查詢的差值計算指定期間流量,不能直接使用累計總量。

健康判斷欄位

採樣週期
30 秒
失敗門檻
連續 3 次
切換冷卻
至少 300 秒
回復條件
連續 2 次成功

流量統計適合當作輔助訊號,延遲與連線結果仍應由主動探測補足。

用腳本完成節點評分與自動切換

腳本不要只比較單次延遲。更穩定的設計是為每個出站保存狀態,例如最近一次成功時間、連續失敗次數、最近統計值、目前評分與是否處於冷卻期。每 30 秒讀取一次 StatsService,並對候選節點執行固定的 HTTPS 或 TCP 測試;當目前節點連續三次失敗,且候選節點至少連續兩次成功,才進入切換程序。

  1. 確認 API 可用

    在 Xray 所在主機執行 statsquery,確認 127.0.0.1:10085 可以回應,並記錄每個出站的完整統計名稱。若 API 無法連線,腳本應直接退出,不可在未知狀態下切換節點。

  2. 建立狀態檔

    在例如 /var/lib/xray-switch/state.json 的受限目錄保存上一次計數、健康結果、目前出站與切換時間。狀態檔權限只允許執行腳本的系統帳號讀寫。

  3. 計算採樣結果

    連續兩次查詢相減得到流量增量,再將主動探測延遲、失敗次數和候選節點權重合併計分。計數器歸零或小於上次數值時,先重設基準,不要產生負的流量增量。

  4. 鎖定切換程序

    使用檔案鎖避免 systemd timer、手動執行和監控服務同時修改核心。取得鎖後再次讀取目前出站,若其他程序已完成切換,當次操作就取消。

  5. 呼叫管理服務

    使用 HandlerService 的出站選擇或管理方法切換至已存在的候選 tag。呼叫後等待 2 至 5 秒,重新查詢 API 與執行探測,只有驗證成功才更新狀態檔。

  6. 保留回復入口

    若切換後候選節點也失敗,回復上一個已知可用的 tag;若所有節點都失敗,停止反覆切換並保留最後結果,交給告警系統處理。

以下是可供腳本使用的狀態資料格式。它不是 Xray 的 API 請求,而是外部程序自己的記錄;將「目前節點」與「上一個節點」分開保存,有助於切換失敗時回復。時間建議使用 UTC 的 ISO 8601 格式,避免夏令時間或跨時區造成冷卻判斷錯誤。

{
  "current": "edge-a",
  "previous": "edge-b",
  "last_switch_at": "2026-08-29T09:30:00Z",
  "cooldown_until": "2026-08-29T09:35:00Z",
  "nodes": {
    "edge-a": {
      "uplink": 1843200,
      "downlink": 73400320,
      "failures": 0,
      "successes": 4,
      "latency_ms": 182
    },
    "edge-b": {
      "uplink": 921600,
      "downlink": 12582912,
      "failures": 1,
      "successes": 3,
      "latency_ms": 96
    }
  }
}

在實作 HandlerService 時,建議使用與 Xray-core 版本相符的 gRPC protobuf 定義,建立服務連線後呼叫對應的出站操作。不要把未確認的 HTTP 路徑當成 API 端點,也不要把一般的 curl http://127.0.0.1:10085 當成可用測試;這個連接埠提供的是 gRPC 服務,不是普通的 REST JSON 介面。若目前客戶端只支援重新載入設定,則可將「產生新設定、語法檢查、原子替換、優雅重啟」作為退回方案。

HandlerService 與多出站架構的取捨

要做到低中斷切換,通常先在 Xray 設定檔中準備多個帶有清楚 tag 的出站,再由路由將需要自動調整的流量送入可管理的選擇結構。這與直接修改單一出站的伺服器位址不同:前者保留多個候選節點,切換時只改選擇結果;後者每次都要重建部分設定,若處理不當容易遺失 TLS、傳輸層或路由欄位。

將 edge-a、edge-b、edge-c 都放在核心設定中,由 API 選擇目前出站。切換速度較快,且不需要在執行期間拼接完整節點 JSON。

適合:固定節點池、低中斷切換

腳本產生新的出站設定,先通過配置檢查,再以原子方式替換並重啟核心。相容性較直觀,但會帶來短暫中斷與設定遺失風險。

適合:節點經常變動、API 不可用

按照 uplink 或 downlink 是否增加判斷節點健康。實作最簡單,但閒置時沒有流量並不代表節點故障,容易造成錯誤切換。

適合:僅作為輔助訊號

多節點架構還要考慮訂閱更新。若節點是由訂閱產生,更新程序可能重建出站 tag;假設腳本固定尋找 edge-a,而客戶端更新後將 tag 改成另一個名稱,StatsService 查詢和 HandlerService 切換都會失敗。因此應固定 tag 生成規則,或在每次更新後重新產生節點對照表,再執行一次 API 健康檢查。

生產環境的回復、記錄與故障限制

自動切換最危險的情況不是一次切換失敗,而是腳本在失敗後不斷切換,形成節點震盪。至少要設定 300 秒冷卻時間、單次執行鎖,以及每天或每小時的最大切換次數。切換前保留目前 tag、切換原因和統計快照;切換後若連續兩次探測失敗,就嘗試回復上一個節點,並將事件標記為 rollback,不要把回復動作當成新的正常切換。

  • 檢查 API 狀態:連不上 127.0.0.1:10085 時停止所有切換,保留現有連線,不要自行重啟核心。
  • 檢查候選節點:候選節點必須先通過至少兩次探測,才可成為切換目標;單次成功不足以排除偶發連通。
  • 檢查切換後流量:等待 2 至 5 秒,再確認新的 outbound 計數器有增加,並執行一次固定目標測試。
  • 限制變更範圍:HandlerService 只操作預先允許的出站 tag,拒絕來自未簽名或不在白名單的節點名稱。
  • 保留原子記錄:先寫入暫存檔,再使用原子替換更新 JSON,避免程序中止時留下半份狀態資料。
{
  "event": "switch",
  "time": "2026-08-29T09:30:00Z",
  "from": "edge-a",
  "to": "edge-b",
  "reason": "probe_failures=3;latency_ms=410",
  "stats_delta": {
    "edge-a": {
      "uplink": 20480,
      "downlink": 163840
    }
  },
  "verified": true,
  "rollback": false
}

錯誤:rpc error: code = Unavailable desc = connection refused

原因與解法:API 入站沒有啟動、連接埠填錯或只監聽其他位址;先檢查 Xray 啟動記錄與 127.0.0.1:10085 的監聽狀態,確認服務可用後再執行腳本。

錯誤:stat not found

原因與解法:統計名稱與出站 tag 不一致,或未開啟 system outbound 統計;複製 statsquery 列出的完整名稱,並核對 policy 與目前實際流量。

錯誤:failed to alter outbound handler

原因與解法:指定的出站不存在、API 未啟用 HandlerService,或目前核心版本不支援該操作;先確認 tag 白名單與 protobuf 版本,必要時改用驗證後重載設定的退回方案。

錯誤:節點不斷在 edge-a 與 edge-b 之間切換

原因與解法:缺少冷卻時間、連續失敗門檻或探測結果穩定期;加入至少 300 秒冷卻、連續 3 次失敗才切換,以及連續 2 次成功才允許回復。

最後,建議將腳本執行記錄送往既有的系統日誌或集中式記錄服務,至少保留時間、目前節點、目標節點、觸發原因、StatsService 差值、探測延遲、API 回應結果與是否回復。不要在日誌中寫入 UUID、私鑰、完整訂閱網址或其他認證資訊。自動化的目標是降低人工操作,不是把敏感節點資料複製到更多位置。

下載用戶端