Xray APIで実現するノード自動選択とJSON設定の実践ガイド

XrayのgRPC APIを使い、通信量や遅延を取得して最適なノードへ自動切り替えする方法を解説します。StatsServiceとHandlerServiceの役割、実用的なJSON設定、スクリプトによるヘルスチェック、API認証、systemd運用、ログの読み方まで、開発者や運用担当者向けに詳しく紹介します。

この記事の概要

手動でノードを切り替えず、XrayのAPIと外部スクリプトを組み合わせて、応答しないノードや遅いノードを自動的に避ける構成を解説します。Xray 25.x系を基準に、APIの公開範囲、統計情報の有効化、gRPCでの取得、遅延測定、スコアリング、JSON設定の分離方法を順番に確認します。APIを有効にしただけで自動選択が完成するわけではないため、Xray本体、測定スクリプト、切り替え処理の役割も分けて説明します。

Xray APIで自動選択を実現する仕組み

XrayのAPIは、ノードの状態を確認したり、統計情報を取得したり、実行中のインバウンドやアウトバウンドを操作したりするための管理インターフェースです。APIを設定しただけで、すべてのノードから最速の接続先が自動的に選ばれるわけではありません。実際の自動選択では、Xrayが通信を処理し、別のスクリプトまたは監視プロセスがAPIから情報を取得し、判定結果に応じて利用ノードを変更するという3つの役割を組み合わせます。

遅延を測定する方法は大きく2種類あります。1つ目は、Xrayの observatory または burstObservatory を利用して、設定したプロキシグループの状態をXray自身に検査させる方法です。2つ目は、管理スクリプトから各ノードへHTTPリクエストを送り、実際の応答時間、接続成功率、直近の失敗回数を記録する方法です。APIのStatsServiceが返すアップロード量・ダウンロード量は通信の利用状況を示す値であり、ネットワーク遅延そのものではありません。この違いを理解しないまま、転送量だけで最速ノードを判断しないことが重要です。

APIをローカル公開統計情報を取得候補を遅延測定スコアを計算出力先を切り替え
127.0.0.1
推奨API待受アドレス
10085
設定例のgRPCポート
3回
候補ごとの最小測定回数
30秒
切り替え判定の最短間隔

自動選択で最も大きな問題は、短時間の一時的な遅延だけでノードを頻繁に切り替えるフラッピングです。たとえばAノードが1回だけ120msになり、次の測定で60msへ戻った場合、即座にBへ変更すると接続中の通信が切れる可能性があります。そのため、最低スコアのノードを選ぶだけでなく、連続して2回以上悪化した場合だけ切り替える、現在のノードより一定割合以上改善した場合だけ変更する、といった安定化条件を入れます。

構成要素 担当する処理 失敗時の影響
Xrayコア 実際のプロキシ通信、ルーティング、APIの提供 すべての通信とAPI操作が停止する
StatsService ユーザー別・アウトバウンド別の転送量を返す 利用量の把握や異常検知ができない
遅延測定スクリプト 候補ノードへ同じURLを送り、応答時間を比較する 自動選択が古い結果または固定ノードに戻る
切り替え処理 バランサーまたはアウトバウンドの有効状態を変更する 測定はできても実際の通信先が変わらない

最小構成のJSON設定を作る

まずAPIを外部ネットワークへ公開しない構成から始めます。API用のインバウンドは 127.0.0.1:10085 で待ち受け、APIタグを持つ通信だけをXray内部の api アウトバウンドへ送ります。管理プロセスが同じサーバー上で動くなら、LANアドレスや 0.0.0.0 で待ち受ける必要はありません。APIポートを外部公開すると、設定変更や統計情報の取得を第三者に許す危険があります。

APIインバウンド

プロトコル
dokodemo-door
アドレス
127.0.0.1
ポート
10085
タグ
api-in

同一ホスト上の管理スクリプトだけが接続できるようにします。

APIサービス

サービス名
HandlerService
統計サービス
StatsService
ルーティング
api-in → api
ログレベル
info

必要なサービスだけを有効にし、不要な管理機能を増やしません。

統計ポリシー

アップロード
statsUserUplink
ダウンロード
statsUserDownlink
アウトバウンド
各タグを監視
対象範囲
必要な項目のみ

StatsServiceは遅延ではなく、転送量と接続の利用状況を確認する用途です。

プローブ設定

probeURL
https://www.gstatic.com/generate_204
interval
60s
subjectSelector
proxy-
許容失敗
2回連続

測定URLは候補ごとに統一し、対象サイトの制限を受けにくい軽量URLを選びます。

{
  "log": {
    "loglevel": "info"
  },
  "api": {
    "tag": "api",
    "services": [
      "HandlerService",
      "StatsService",
      "RoutingService"
    ]
  },
  "inbounds": [
    {
      "tag": "api-in",
      "listen": "127.0.0.1",
      "port": 10085,
      "protocol": "dokodemo-door",
      "settings": {
        "address": "127.0.0.1"
      },
      "streamSettings": {
        "network": "tcp"
      }
    }
  ],
  "routing": {
    "rules": [
      {
        "type": "field",
        "inboundTag": [
          "api-in"
        ],
        "outboundTag": "api"
      }
    ],
    "balancers": [
      {
        "tag": "auto-proxy",
        "selector": [
          "proxy-"
        ]
      }
    ]
  },
  "stats": {},
  "policy": {
    "levels": {
      "0": {
        "statsUserUplink": true,
        "statsUserDownlink": true
      }
    },
    "system": {
      "statsInboundUplink": true,
      "statsInboundDownlink": true,
      "statsOutboundUplink": true,
      "statsOutboundDownlink": true
    }
  }
}

この例では、api タグのアウトバウンド本体と、実際に利用する proxy-asiaproxy-eu などのアウトバウンド定義を省略しています。実運用では、サブスクリプションから生成した各ノードを個別のタグに割り当て、タグの先頭を proxy- に統一します。タグ名はJSON内で一文字でも異なると一致しないため、ハイフン、大文字、小文字を含めて統一してください。

RoutingService を有効にする場合は、XrayのバージョンとAPIクライアントが対応していることを確認します。不要なら最初は HandlerServiceStatsService だけに絞り、読み取りと簡単な管理操作から検証するほうが安全です。設定変更後は xray -test -config config.json 相当の構文チェックを実行し、JSONの末尾カンマ、重複タグ、存在しないサービス名を確認してから再起動します。

遅延を測定し、ノードのスコアを計算する

StatsServiceから取得できる値は、アウトバウンドごとのアップロード量とダウンロード量、ユーザー単位の統計値です。値が増えていることは、そのノードが通信に使われたことを示しますが、応答速度が良いことを意味しません。遅延ランキングを作る場合は、StatsServiceを利用量の確認と異常検知に使い、接続時間は別のプローブで測定します。

候補ノードを比較するときは、同じURL、同じHTTPメソッド、同じタイムアウト、同じ測定回数を使います。たとえば各ノードに対して10秒のタイムアウトで3回測定し、最小値ではなく中央値を採用します。1回目だけDNSキャッシュの影響を受ける場合があるため、測定結果をそのまま平均するより、中央値と失敗回数を組み合わせるほうが安定します。

評価項目 例の重み 判定方法 注意点
中央値遅延 50% 3回以上のHTTP応答時間を比較 URLまでの経路を固定する
接続成功率 30% 直近10回の成功数を割合にする タイムアウトは失敗として記録する
転送量の増加 10% StatsServiceの値から利用中か確認 速度の直接測定値として扱わない
連続失敗数 10% 2回以上で大きく減点する 一時的な失敗と継続障害を区別する

スコアの計算例は、遅延を基準化して latencyScore = min(100, medianMs / 5)、成功率を failureScore = (1 - successRate) * 100 とし、次のように減点する方法です。数値は環境ごとに調整してください。

totalScore =
  latencyScore * 0.50 +
  failureScore * 0.30 +
  usagePenalty * 0.10 +
  consecutiveFailurePenalty * 0.10

切り替え条件:
現在のスコア - 新候補のスコア >= 15
かつ新候補の測定成功回数 >= 2
かつ前回の切り替えから30秒以上経過

HTTP応答、成功率、連続失敗、転送量を組み合わせて、環境に合った判定式を作れます。現在のノードを一定時間維持するクールダウンも実装しやすい方法です。

適した用途:複数地域のノード、細かい監視、運用サーバー

Xrayの設定にprobeURL、probeInterval、subjectSelectorを指定し、比較的少ない外部処理で候補の状態を検査できます。利用できる判定基準とAPIの扱いは、使用中のXrayビルドで確認が必要です。

適した用途:標準機能を中心にした構成、簡単な死活監視

設定が最も簡単で、切り替えによる予期しない通信断も少なくなります。ただし、障害発生時に利用者が画面を操作する必要があり、自動復旧には向きません。

適した用途:候補が少ない環境、検証中の初期構成

結論:最速ではなく、失敗しにくいノードを選ぶ

中央値が55msのノードでも直近10回の成功率が100%なら、中央値35msだが成功率80%のノードより実利用では安定することがあります。自動選択の目的を最低遅延ではなく、遅延と可用性のバランスとして設計してください。

APIを使った切り替え処理を実装する手順

動作確認では、いきなり全ノードを自動変更しないでください。まずAPIへ接続できること、StatsServiceから数値を取得できること、測定スクリプトが候補ごとに結果を保存できることを順番に確認します。切り替え処理は最後に有効化し、最初の数時間はログだけを出すドライランにします。

  1. APIを構文検査

    設定ファイルを保存し、XrayのテストモードでJSONを検査します。APIインバウンド、APIタグ、ルーティングの3つが同時に存在することを確認します。

  2. 統計取得を確認

    同じサーバー上で xray api statsquery --server=127.0.0.1:10085 --pattern= を実行し、応答が返るか確認します。転送が発生していない場合に値が0でも、API接続成功とは分けて記録します。

  3. 候補を測定

    proxy- で始まるアウトバウンドを候補として読み込み、同一URLへ3回ずつプローブします。結果には時刻、タグ、応答時間、成功または失敗、エラー内容を保存します。

  4. ドライランする

    最良候補を表示するだけにし、実際の切り替えAPIは呼び出しません。30秒間隔で10回以上実行し、スコアの変動と候補の入れ替わりを確認します。

  5. 変更条件を有効化

    改善幅15点、成功測定2回、クールダウン30秒などの条件を満たした場合だけ切り替えます。切り替え前後のタグと理由を必ずログへ出力します。

  6. 復旧経路を残す

    API操作に失敗した場合は現在のノードを維持し、すべての候補を無効にしない設計にします。固定ノードへ戻すコマンドと、最後に正常だった設定のバックアップを用意します。

切り替え方法は、使用するXrayのAPI機能と構成によって異なります。既存のバランサーや観測機能を利用する場合は、その機能が参照するタグとセレクターを変更します。アウトバウンドを動的に追加・削除する方法では、HandlerServiceの対象メソッド、認証経路、設定の永続化方法を事前に確認してください。実行中のメモリ上の設定を変更しても、Xrayを再起動した際に元のJSONへ戻ることがあります。

ドライランのログ例:
2026-08-29T10:00:00Z candidate=proxy-asia median=62ms success=10/10 score=18
2026-08-29T10:00:00Z candidate=proxy-eu median=48ms success=8/10 score=34
2026-08-29T10:00:00Z current=proxy-asia best=proxy-asia action=keep reason=improvement<15

切り替えを許可する例:
2026-08-29T10:01:00Z current=proxy-asia best=proxy-eu
2026-08-29T10:01:00Z action=switch reason=score_gap=21 cooldown=expired

ノード切り替え時に既存のTCP接続が新しいノードへ移動するわけではありません。すでに確立した接続は旧アウトバウンドに残り、新しく作られた接続から新しい候補が使われる構成もあります。ブラウザーの複数接続、長時間のダウンロード、WebSocketなどでは、切り替え直後も旧経路が残っているように見えることがあります。判定ログだけでなく、新規接続を使った実通信で結果を確認してください。

運用時の安全対策とトラブル対処

API自動化は便利ですが、誤判定を短時間に繰り返すと、ノードの切り替え自体が障害になります。監視スクリプトが停止した場合は現在のノードをそのまま維持し、判定結果が空の場合も全ノードを無効にしないでください。API応答が遅い、測定URLが一時的にエラーを返す、DNSだけが失敗するといったケースを、ノード全体の障害と同一視しないことが大切です。

StatsServiceの転送量だけで最速ノードを選べますか?

選べません。転送量は利用状況を示す統計であり、遅延や帯域の直接測定値ではありません。StatsServiceは利用中の確認と異常検知に使い、応答時間は統一したプローブで測定してください。

APIへ接続できるのにノードが切り替わりませんか?

API接続と切り替え処理は別です。候補タグがbalancerのselectorに一致するか、ドライランが解除されているか、改善幅とクールダウン条件を満たしているか、実行中の構成を変更するAPIメソッドが正しいかを確認します。

遅延測定のたびにノードが変わって通信が不安定になります。

測定間隔を30秒以上にし、連続2回以上の悪化条件と15点程度の改善幅を追加します。現在のノードを最低5分維持するクールダウンを設定すると、短い揺らぎによる頻繁な変更を抑えられます。

Xrayを再起動すると自動選択が解除されます。

実行中のAPI変更だけでなく、元のJSON、監視スクリプトの起動設定、最後に正常だったノードタグを保存してください。再起動後にAPIの待受、候補タグ、バランサーのselectorを順番に確認します。

結論:自動化する前に、手動で同じ判定を再現する

3つのノードを固定した環境で10回測定し、スコア表から選ばれる候補が人間の判断と大きく違わないことを確認してから、切り替え操作を有効にしてください。測定、評価、変更を別々にログへ残すと、誤判定が起きた時点を特定しやすくなります。

導入前に確認するチェックリスト

完成した構成では、Xray本体がプロキシ通信を担当し、APIが管理情報を返し、測定スクリプトが候補の状態を比較し、切り替え処理が十分な差がある場合だけ経路を変更します。この役割分担を崩さなければ、Xrayの設定を大きく書き換えずに自動選択を段階的に導入できます。

Xray APIによるノード自動選択は、「APIを有効にして最小遅延のノードへ自動接続する」単純な機能ではありません。統計情報、実測遅延、成功率、切り替え条件、復旧経路を個別に設計し、まず観測、次に評価、最後に変更という順番で有効化することで、予期しない接続断や頻繁なノード変更を避けられます。

V2Rayクライアントのダウンロード