Xray API 節點自動選優:JSON 設定與腳本實戰

想讓 Xray 依延遲與健康狀態自動挑選節點?本指南以 gRPC API 為核心,說明 StatsService、HandlerService、路由標籤與 JSON 設定的搭配方式,並提供可套用的測速腳本、權限隔離、systemd 常駐及日誌除錯方法,協助建立更穩定的自動化代理架構。

本篇專為熟悉 Xray 設定檔的開發者與網路管理員撰寫,示範如何透過 Xray API 讀取流量統計、搭配節點健康檢查與腳本排程,建立可控的自動選線機制。內容以 Linux 上的 Xray-core 1.8.x~1.9.x 常見配置為例,涵蓋 API 入站、JSON 結構、候選節點比較、切換策略、權限限制,以及長時間執行時的記錄與排錯方法。

本文速覽

讀完後,你可以建立僅監聽 127.0.0.1:10085 的 Xray API,使用 StatsService 取得節點流量與錯誤指標,再由腳本按照延遲、成功率與冷卻時間選出候選節點。文中也會區分「由 Xray balancer 自動選線」與「外部腳本修改設定後重載」兩種方案,避免把 API、路由規則與系統服務混為一談。

先釐清 Xray API 能做什麼

Xray API 是以 gRPC 提供的管理介面,不是另一條代理協定,也不會自動替你測試所有節點。API 必須先在設定檔中啟用對應服務,外部程式再透過 gRPC 送出查詢或管理要求。常見服務包括 StatsServiceHandlerServiceRoutingServiceLoggerService

自動選優通常包含三個獨立環節:第一是取得數據,例如上行、下行、連線錯誤或探測延遲;第二是按照門檻排除不健康節點;第三是讓流量真正改用新節點。前兩步可以透過 API 完成,但第三步不一定等同於呼叫一個「切換目前節點」按鈕。若使用 balancer,Xray 會在多個出站之間選擇;若由腳本修改 JSON,則通常需要透過管理服務重建出站,或讓 systemd 重新載入設定。

建立 API 入站讀取節點指標套用健康門檻計算候選分數更新代理出站
10085
API 常用連接埠
127.0.0.1
建議監聽位址
3 次
最低健康樣本數
60 秒
常見檢查週期

結論:先分離「監測」與「切換」

健康檢查只負責回答哪個節點目前較可靠,切換程序則負責改變實際出站。將兩者分開記錄,才能判斷是測試不準、評分錯誤,還是設定更新沒有生效。

建立安全的 API JSON 設定

以下是可嵌入主設定檔的基本結構。API 入站使用 dokodemo-door 接收本機管理連線,並以 api 標籤識別。StatsService 需要配合 policy 開啟統計,否則執行查詢時可能只得到空結果。

{
  "log": {
    "loglevel": "warning",
    "error": "/var/log/xray/error.log",
    "access": "/var/log/xray/access.log"
  },
  "api": {
    "tag": "api",
    "services": [
      "HandlerService",
      "LoggerService",
      "StatsService",
      "RoutingService"
    ]
  },
  "stats": {},
  "policy": {
    "levels": {
      "0": {
        "statsUserUplink": true,
        "statsUserDownlink": true
      }
    },
    "system": {
      "statsInboundUplink": true,
      "statsInboundDownlink": true,
      "statsOutboundUplink": true,
      "statsOutboundDownlink": true
    }
  },
  "inbounds": [
    {
      "tag": "api-in",
      "listen": "127.0.0.1",
      "port": 10085,
      "protocol": "dokodemo-door",
      "settings": {
        "address": "127.0.0.1"
      }
    }
  ],
  "routing": {
    "rules": [
      {
        "type": "field",
        "inboundTag": ["api-in"],
        "outboundTag": "api"
      }
    ]
  }
}

這段設定的重點不是固定使用 10085,而是讓腳本與 Xray 共享一個明確、固定且只供本機使用的管理端點。若伺服器上已有其他服務使用 10085,可以改成 10086,但必須同步修改腳本、監控工具與防火牆規則。API 不應監聽 0.0.0.0,也不應直接暴露至公網;Xray API 本身並非以帳號密碼作為通用的安全邊界。

API 入站

協定
dokodemo-door
位址
127.0.0.1
連接埠
10085
用途
本機 gRPC 管理

只允許同一台主機上的腳本連線,不要為了遠端操作而開放所有網卡。

統計策略

使用者統計
statsUserUplink
流量方向
上行與下行
服務
StatsService
記錄層級
warning

只開啟需要的統計欄位,避免在高流量伺服器上產生過多記錄與查詢負擔。

完成設定後先執行設定檢查,再重新啟動服務。常見命令如下,實際執行檔位置應以發行版安裝方式為準。

sudo xray run -test -config /etc/xray/config.json
sudo systemctl restart xray
sudo systemctl status xray --no-pager
ss -lntp | grep 10085

比較 balancer 與外部腳本兩種方案

如果節點格式穩定,而且需求是依探測結果在多個出站中選擇,優先考慮 Xray 原生的 balancer 與 observatory。這種方式讓核心維持節點清單,路由規則將指定流量送入 balancer,再由策略選擇可用出站。若需求包含自訂評分、每日報表、失敗冷卻、跨多個 Xray 程序或根據業務時段切換,外部腳本會更容易維護。

節點與路由保留在 Xray 設定中,由核心根據 observatory 或 balancer 策略處理選線,切換時通常不必重啟整個服務。

適合:固定節點池、低延遲切換、少量自訂邏輯

腳本讀取統計、執行外部測試,再修改出站或產生新設定。判斷條件最自由,但必須自行處理併發、回滾與服務重載。

適合:自訂評分、報表、跨程序控制

以模板保留節點資料,腳本只更新目前啟用的出站並由 systemd 重啟。實作容易理解,但切換期間會有短暫中斷。

適合:切換頻率低、可接受數秒重連

不要只用單次延遲作為唯一標準。延遲低的節點可能有高封包遺失率,或在實際下載時吞吐量很差。較穩妥的分數可以由多項指標組成,例如延遲占 40%、近 5 分鐘成功率占 40%、錯誤懲罰占 20%。分數計算只是篩選工具,仍應設定硬性淘汰門檻:連續 3 次連線失敗、延遲超過 800 毫秒,或最近 5 分鐘錯誤率高於 20% 時,暫時移出候選集合。

動手製作 API 查詢與切換腳本

以下流程先完成「讀取統計與評估」,再決定是否修改設定。若只需要查詢 Xray 的統計資料,可以使用隨 Xray 提供的 API 命令列工具;不同核心版本的命令參數可能略有差異,請先執行 xray api statsquery --help 確認。查詢時可先列出目前可用的統計項目,再指定某個 outbound 或 user 的計數器。

  1. 準備候選清單

    在設定中為每個代理出站設定唯一的 tag,例如 node-hk-01node-sg-01node-jp-01,不要使用會隨訂閱更新而改變的顯示名稱。

  2. 測試 API 連線

    在本機執行 xray api statsquery --server=127.0.0.1:10085,若得到統計項目清單,表示 API 入站、路由與服務名稱基本正常。

  3. 記錄健康數據

    腳本每 60 秒執行一次,保存時間戳、節點標籤、探測延遲、成功率與最近錯誤,不要只覆寫一個無法追溯原因的數字。

  4. 套用滯後門檻

    新候選分數至少高於目前節點 15%,且目前不在 180 秒冷卻期內,才進入切換流程。

  5. 驗證新出站

    更新後先檢查 systemctl is-active xray、監聽連接埠與核心錯誤記錄,再以固定測試目標發出一次實際請求。

#!/usr/bin/env bash
set -euo pipefail

API="127.0.0.1:10085"
STATE="/var/lib/xray/selector.state"
LOG="/var/log/xray/selector.log"

now=$(date +%s)
stats=$(xray api statsquery --server="$API" 2>/dev/null) || {
  printf '%s api_unreachable\n' "$(date -Is)" >> "$LOG"
  exit 1
}

printf '%s stats_received bytes=%s\n' \
  "$(date -Is)" "${#stats}" >> "$LOG"

# 實際專案中於此解析節點指標、計算分數與冷卻時間。
# 只有通過滯後門檻,才更新模板並執行受控重載。
printf '%s\n' "$now" > "$STATE"

這段 shell 只示範 API 可達性與狀態保存,沒有假裝用文字搜尋就能可靠解析所有 protobuf 回應。正式環境應使用 Xray 對應版本的 gRPC client,例如以 Python、Go 或其他支援 gRPC 的程式讀取結構化結果。若採用 JSON 模板重建方式,先產生暫存檔,再執行 xray run -test -config /etc/xray/config.next.json;測試成功後以原子方式替換正式設定,最後才執行 reload 或 restart。

tmp="/etc/xray/config.next.json"
sudo xray run -test -config "$tmp"
sudo install -o root -g root -m 0644 "$tmp" /etc/xray/config.json
sudo systemctl restart xray
sudo systemctl is-active --quiet xray

Linux 長時間執行與排錯

腳本不應以互動式終端機啟動後就放著不管。建議建立專用系統使用者、限制設定檔與狀態檔權限,並使用 systemd timer 或 service 管理週期。腳本需要讀取 Xray API,但不應擁有修改整個系統的多餘權限;若確實需要重啟服務,可透過限制命令的 sudoers 規則處理,而不是給予完整 root 權限。

[Unit]
Description=Xray node selector
After=xray.service
Requires=xray.service

[Service]
Type=oneshot
User=xray-selector
ExecStart=/usr/local/sbin/xray-node-selector

[Install]
WantedBy=multi-user.target
[Unit]
Description=Run Xray node selector every minute

[Timer]
OnBootSec=2min
OnUnitActiveSec=60s
Persistent=true

[Install]
WantedBy=timers.target

啟用後可使用 systemctl list-timers 確認排程,使用 journalctl -u xray-node-selector.service -n 50 --no-pager 查看最近執行結果。Xray 本身則查看 journalctl -u xray -n 100 --no-pager,並將 API 查詢失敗、候選數量為零、切換原因與重啟結果分開記錄。記錄中不要寫入完整 VLESS UUID、VMess 使用者識別或訂閱網址,避免日誌變成憑證外洩來源。

API 連得上,但查不到節點統計怎麼辦?

先確認 StatsService 已加入 api.services,再檢查 policy 是否啟用使用者或系統流量統計。若只剛啟動核心,計數器可能尚未產生;先發出實際請求後再查詢。

腳本一直在兩個節點之間切換,怎麼處理?

加入至少 15% 的分數滯後、180 秒冷卻時間與連續 3 次樣本要求。不要因為單次延遲低 5 毫秒就切換,否則網路抖動會造成反覆重載。

重啟後設定檔有效,但節點仍無法使用?

查看出站 tag 是否與路由規則完全一致,並檢查 Reality、TLS、WebSocket 路徑與 UUID 是否在模板替換時被改壞。設定語法通過不代表遠端握手參數正確。

可以把 10085 開放給監控主機嗎?

不建議直接開放。較安全的方式是讓監控主機透過 SSH 受限轉發連到本機 API,或在可信任的內部網段搭配防火牆白名單與額外隔離;不要把管理服務暴露到公網。

結論:自動選優的核心是可回退,而不是切換得快

一套可靠機制應能回答「為何選這個節點」「何時判定失敗」以及「切換後如何回到上一個節點」。先完成健康數據、冷卻時間、原子更新與回退記錄,再逐步增加更複雜的評分模型,長期穩定性通常比頻繁追逐最低延遲更重要。

V2Ray用戶端下載