Xray gRPC API进阶配置:脚本实现节点自动择优与流量监控

通过 Xray gRPC API 将节点状态采集与出站管理接入自动化脚本。文章详解 StatsService、HandlerService 的配置和调用方式,提供可直接参考的 JSON 示例,并梳理节点择优、安全隔离与常见排障步骤。

本文速览

适合已经使用 Xray、希望把流量采集与节点管理纳入自动化流程的进阶用户。本文说明如何启用 StatsService 和 HandlerService、按出站标签读取累计流量,并设计带冷却与切换门限的节点择优逻辑。示例以本机 gRPC API 端口 10085 为例;API 只提供管理接口,不会自动替你完成测速、健康判断或策略决策。

先划清职责:API 提供数据与操作,不负责择优

Xray 的 gRPC API 是内核管理接口。启用 StatsService 后,外部程序可以查询已记录的统计数据;启用 HandlerService 后,程序可以在运行期间管理入站和出站处理器。API 本身不会定时探测服务器、判断某个节点是否“最快”,也不会替应用自动切换网络路径。

要实现自动择优,脚本至少需要完成三件事:采集候选节点的连接表现,按一致的规则计算候选分数,再通过受控的管理操作改变实际使用的出站。流量统计则是另一条数据链:它能帮助判断某个出站近期是否有流量,却不能单独证明这个出站延迟低、丢包少或仍可正常访问目标网站。

API 读取统计 脚本探测节点 计算候选分数 更新目标出站 复核连接结果

因此应把“节点健康”和“流量使用量”作为两种指标管理。连接探测可采用 TCP 建连或实际业务请求;统计值来自 Xray 运行期间的计数器。二者需要按相同时间窗口汇总,不能把累计流量误认为瞬时速率,也不能用节点列表中的一次延迟测试代替持续健康检查。

配置 StatsService 与 HandlerService

下面是传统 JSON 配置中的相关片段,用于说明 API 入站、服务注册、路由放行和流量统计之间的关系。它不是可直接覆盖现有配置的完整文件:应把对应字段合并到当前 Xray 配置,检查原有路由顺序,并先备份客户端生成的配置。若客户端会在启动时重写配置,直接修改生成文件可能在下一次启动后失效。

API 管理入口

API 标签
api
服务
StatsService、HandlerService
监听地址
127.0.0.1
示例端口
10085
路由目标
outboundTag: api

监听回环地址可将管理入口限制在本机;不要为了远程调用而直接改成公网监听。

统计计数开关

统计对象
stats
出站上行
statsOutboundUplink
出站下行
statsOutboundDownlink
统计粒度
按出站标签查询
运行条件
请求经过对应出站

只注册 StatsService 而没有开启统计计数,查询可能没有预期数据。

{
  "api": {
    "tag": "api",
    "services": [
      "StatsService",
      "HandlerService"
    ]
  },
  "stats": {},
  "policy": {
    "system": {
      "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"
      }
    ]
  }
}

此片段把 API 入站限定在本机,并用路由规则将该入站的请求交给内部 API 出站。若已有 routing.rules,应将 API 规则放在不会被更早规则截断的位置;同时检查实际使用的配置格式与内核版本。Xray 启动失败时,优先查看错误日志中的 JSON 解析错误、端口占用和路由标签问题,不要先同时改动节点配置。

出站计数是内核运行期间按标签累积的字节数。若要分别观察多个节点,节点出站需要有稳定且唯一的 tag,例如 node-a、node-b。标签应由管理脚本维护,不要把订阅备注、服务器地址或认证信息直接拼进标签。统计开关和 API 服务都配置正确后,仍需产生经过目标出站的流量,查询结果才有变化。

动手操作:建立安全的节点择优闭环

开始前先挑出两到三个候选节点,在客户端逐个确认连接可用;确保每个候选对应明确的出站标签,并准备一个稳定、允许探测的测试目标。不要用一次延迟结果直接切换生产流量。以下步骤以“脚本测量候选节点,再更新一个固定的活动出站”为思路,具体 HandlerService 请求对象应按部署所用 Xray 版本的 protobuf 定义生成。

  1. 固定 API 入口

    将 API 入站设为 127.0.0.1:10085,启用所需服务,启动 Xray 后检查日志。先用本机客户端建立 gRPC 连接;如果连不上,检查端口是否监听、是否被其他进程占用,以及 API 入站是否进入正确的路由。

  2. 登记候选标签

    为候选出站配置固定标签,例如 node-a 和 node-b,另设一个业务使用的稳定标签,例如 auto-proxy。记录节点配置的来源和版本,避免脚本切换时把订阅更新后的旧参数继续写回内核。

  3. 重复测量

    每个候选在相同目标、相同接入网络下测量至少 3 次,记录中位延迟、失败次数和实际请求是否成功。测量时使用对应候选出站,不能只测 API 本地端口或对节点地址做 ping 后就认定业务连接可用。

  4. 计算切换条件

    先剔除请求失败或连续超时的候选,再比较延迟和稳定性。为当前节点设置保持时间与切换门限,例如候选连续 3 轮胜出且中位延迟至少改善 20% 才切换;这些是策略示例,应按业务容忍度调整。

  5. 调用并验证

    通过 HandlerService 对受控出站执行对应操作,随后确认新配置生效,再用独立业务请求验证连接。切换失败时保留原出站配置并回滚;把所选标签、时间、测量结果和 API 返回状态写入本地日志。

下面是选择逻辑的 Python 风格伪代码,着重展示决策边界。probe、replace_active_outbound 和 read_stats 是需要按实际探测方式及生成的 gRPC 客户端实现的适配函数,并非 Python 标准库或 Xray 自带的同名函数。HandlerService 的请求字段必须与正在运行的 Xray API 定义匹配,不能把这段伪代码误当成可直接运行的 protobuf 客户端。

CANDIDATES = ["node-a", "node-b", "node-c"]
ACTIVE_TAG = "auto-proxy"
MIN_IMPROVEMENT = 0.20
REQUIRED_WINS = 3

def choose_candidate(current, history):
    results = {}
    for tag in CANDIDATES:
        samples = [probe(tag) for _ in range(3)]
        successes = [x for x in samples if x.ok]
        if len(successes) < 2:
            continue
        results[tag] = median(x.latency_ms for x in successes)

    if not results:
        return current

    best = min(results, key=results.get)
    if best == current:
        return current

    current_score = results.get(current)
    if current_score is None:
        return best if history.wins(best) >= REQUIRED_WINS else current

    improvement = (current_score - results[best]) / current_score
    if improvement >= MIN_IMPROVEMENT and history.wins(best) >= REQUIRED_WINS:
        replace_active_outbound(
            api="127.0.0.1:10085",
            active_tag=ACTIVE_TAG,
            source_tag=best
        )
        return best

    return current

伪代码中的“连续胜出”应由脚本保存短期测量历史,而不是每轮重新开始计数。生产环境还应添加最短切换间隔、失败重试上限和变更锁,防止两个定时任务同时改写出站。若使用订阅自动更新,脚本应在更新后重新读取候选节点配置,并验证目标出站仍存在;不要依赖可能变化的节点列表顺序。

判断:稳定性优先于一次性的最低延迟

先用成功率和连续测量筛掉不稳定节点,再比较中位延迟。加入 20% 改善门限与连续 3 轮胜出条件,可以减少微小波动导致的频繁切换;如果当前节点已经失败,则应优先执行故障切换,而不是等待门限满足。

查询流量统计,并把累计值变成速率

StatsService 返回的是计数器,不是实时带宽。脚本应每隔固定时间查询同一统计项,保存前后两次的累计字节数,再用差值除以时间间隔计算平均速率。比如每 10 秒采样一次,若上行计数从 8,000,000 增至 8,500,000 字节,窗口内平均上行约为 50,000 字节/秒;这个数值只描述该窗口的累计变化,不代表后续速度保证。

# 命令行调用方式因 Xray 构建版本而异。
# 先检查当前二进制提供的 API 子命令与参数:
xray api statsquery --help

# 常见版本可按出站标签查询匹配统计项:
xray api statsquery \
  --server=127.0.0.1:10085 \
  -pattern 'outbound>>>node-a>>>traffic'

如果当前版本提供 statsquery 子命令,可按帮助信息确认 --server、匹配模式以及是否支持重置等参数;不同发行版的命令行封装可能有差异。也可以使用与 Xray API 定义相匹配的 gRPC 客户端调用 StatsService 查询方法。不要把重置计数器当成默认采样方式:重置会影响其他监控程序的累计结果,通常用两次快照做差更适合共享监控。

观察项可说明什么不能据此断定
出站上行累计字节统计周期内经该出站发送的数据量节点当前可用带宽或上传速度上限
出站下行累计字节统计周期内经该出站接收的数据量用户实际看到的单个文件下载速度
连续窗口差值窗口内平均字节变化率,可发现流量是否增长应用层有效载荷速率,因协议开销和统计边界可能不同
计数没有变化该统计项在采样窗口内没有记录到新流量节点必然离线,流量也可能走了其他出站

统计项通常由出站标签、流量方向等字段组合标识。实际名称应通过当前 Xray 版本返回的统计项确认,再匹配标签读取;标签不一致、流量没有经过目标出站、统计策略未开启,都会造成查询为空或数值不增长。流量计数在内核重启后可能重新开始,监控程序应识别计数器归零,并将其视为新一轮基线,而不是产生负速率。

安全边界与常见问题

gRPC API 有能力查看统计并改变运行中的处理器,应按管理接口保护,而不是当作普通代理端口。默认只绑定回环地址;如果确实需要跨主机管理,应通过受控的内网通道、访问控制和网络防火墙限制来源,并为访问凭据、日志与配置文件设置合适权限。不要将 API 端口转发到公网,也不要把订阅链接、用户标识、节点认证信息或完整配置写入公开日志。

查询成功,但统计值一直是零,先查哪里?

确认 stats: {}、policy.system.statsOutboundUplink 与 statsOutboundDownlink 已启用;再确认测试请求确实经过正在查询的出站标签。先产生一笔明确的代理流量,再间隔数秒查询,排除采样窗口太短。

API 端口连不上,是不是节点不可用?

不一定。API 连接的是本机管理入站,不是远端代理节点。检查 Xray 启动日志、127.0.0.1:10085 是否监听、端口是否冲突,以及配置中 API 入站的路由规则是否正确。

HandlerService 返回成功,流量为什么没切过去?

检查业务路由实际使用的标签是否就是被修改的出站标签,并确认活动连接是否需要重新建立。管理操作成功只说明 API 接受了请求,不等于现有连接都迁移,也不等于目标网站请求已通过新节点。

能不能按流量大小自动选节点?

可以把流量作为成本或限额指标,但不能只选累计字节最少的节点:新节点的累计值通常天然较低。应比较同一采样窗口的增量、业务成功率和延迟,并结合服务套餐的限额定义切换规则。

部署前先在测试配置上验证一次完整闭环:确认 API 可连、统计计数增长、候选探测能区分失败、出站变更可回滚,再将脚本接入定时任务。每次自动切换都应记录前后标签与原因;若日志显示配置更新成功但业务请求失败,先恢复已知可用出站,再逐项检查目标标签、节点参数和路由规则。

下载v2rayN