当节点数量增加后,手动逐个测试、复制配置和切换活动节点很快就会变成重复劳动。Xray 提供的 gRPC API 可以让外部程序读取运行状态、查询流量统计,并通过 HandlerService 动态增加或移除出站;再配合独立的健康检查脚本,就能把“测速—筛选—切换—记录”串成一个可持续运行的流程。需要先说明的是,Xray API 本身不是完整的节点调度平台,API 只负责执行控制命令,测速规则、失败阈值、切换冷却时间和权限保护仍然需要由脚本设计。
本文面向能够读写 Xray JSON、了解 Linux 命令行和 gRPC 基础的开发者与运维人员,使用本机回环地址开放 API,搭建节点健康检查与动态切换方案。你将看到 API 配置、节点 JSON、脚本流程、权限控制、日志处理和 systemd 长期运行方式,并能根据节点数量和业务风险选择“动态修改”或“生成配置后重启”两种实现路径。
一、先理解 Xray API 能做什么
Xray 的 API 通常通过 gRPC 提供服务。核心启动后,外部程序连接 API 入站端口,再调用已经启用的服务。常见的 HandlerService 用于管理入站和出站,StatsService 用于读取流量统计;部分版本还提供与路由、观测相关的服务。不同 Xray-core 版本的 proto 定义和可用命令可能存在差异,因此脚本部署前应先确认实际核心版本、服务名称和客户端库是否匹配。
核心保持运行,脚本通过 HandlerService 管理出站,切换速度快,适合节点数量较少、能够接受短暂控制窗口的服务。
适合:个人网关、低延迟切换、少量节点
脚本生成完整配置文件,校验通过后执行平滑重启。流程容易审计,也更适合需要严格保留配置快照的环境,但重启期间可能有短暂连接中断。
适合:生产配置、节点较多、变更需留痕
只在 v2rayN 或其他客户端中更新节点,不改变服务器端的 Xray 运行状态。实现简单,但不能替代服务器上的自动健康检查。
适合:桌面端手动维护、临时测试
结论:API 负责执行,脚本负责决策
不要把“API 返回成功”误认为“节点已经可用”。AddOutbound 成功只表示核心接受了出站对象,真正的网络可用性仍需通过代理请求、超时统计和连续失败次数确认。自动切换逻辑至少要包含成功阈值、失败阈值和冷却时间。
二、用 JSON 开放受保护的本机 API
API 配置通常由 api、inbounds 和 routing 三部分组成。下面的片段展示一个只监听本机回环地址的 API 入站。api.tag 必须与路由规则中的 inboundTag 对应,outboundTag 则应指向名为 api 的出站。若配置中没有 API 出站,调用可能建立连接却无法正常执行服务请求。
{
"api": {
"tag": "api",
"services": [
"HandlerService",
"StatsService"
]
},
"inbounds": [
{
"tag": "api-in",
"listen": "127.0.0.1",
"port": 10085,
"protocol": "dokodemo-door",
"settings": {
"address": "127.0.0.1"
}
}
],
"outbounds": [
{
"tag": "api",
"protocol": "freedom",
"settings": {}
},
{
"tag": "active",
"protocol": "freedom",
"settings": {}
}
],
"routing": {
"rules": [
{
"type": "field",
"inboundTag": [
"api-in"
],
"outboundTag": "api"
}
]
}
}
上面的 active 只是结构示意,不代表真正的节点配置。实际使用时,节点出站应放入单独的 JSON 模板或数据库,由脚本按标签载入。API 端口不应监听在 0.0.0.0,也不要直接把 10085 暴露到公网。Xray 的 gRPC API 一旦被未授权访问,攻击者可能读取统计、修改处理器或改变出站,风险远高于普通代理端口。
API 入站
- 协议
- dokodemo-door
- 监听
- 127.0.0.1
- 端口
- 10085
- 服务
- HandlerService、StatsService
只允许同一台服务器上的脚本访问,不为远程管理开放端口。
节点出站
- 节点标签
- node-hk-01
- 协议
- VLESS 或 VMess
- 端口
- 以服务端实际值为准
- 切换标识
- active 或 balancer 标签
每个标签必须唯一,脚本不能用模糊名称替代精确标签。
三、设计节点 JSON 与健康检查指标
自动选优不能只保存一个节点链接。脚本至少应为每个节点保存唯一标签、协议参数、测试地址、超时时间、连续失败次数和最近一次成功时间。节点配置和运行状态最好分离:配置文件记录静态参数,SQLite、JSON 状态文件或内存对象记录动态分数。这样测速失败不会直接污染原始节点数据。
{
"tag": "node-sg-01",
"protocol": "vless",
"address": "edge.example.net",
"port": 443,
"uuid": "替换为实际 UUID",
"network": "tcp",
"security": "reality",
"health": {
"url": "https://example.com/generate_204",
"timeout_ms": 5000,
"fail_limit": 3,
"cooldown_sec": 60
}
}
测速请求应经过待测节点,而不是直接从服务器访问目标地址。常见做法是为每个节点建立临时出站和对应的本地测试入口,或者使用脚本生成一次性测试配置。若节点已经作为固定出站载入,也可以让测试请求按照节点标签路由,再用 curl 的代理参数访问稳定的小响应地址。测试目标应返回较小内容,避免把测速变成大量下载。
| 指标 | 建议记录 | 判断用途 | 不要误判为 |
|---|---|---|---|
| 连接耗时 | 毫秒,保留 3 次结果 | 筛掉握手明显异常的节点 | 长期下载速度 |
| HTTP 状态 | 200、204 或业务允许状态 | 确认代理请求确实完成 | 节点全部协议能力 |
| 连续失败 | 次数与最后错误 | 判断是否触发摘除 | 单次网络抖动 |
| 恢复次数 | 连续成功次数 | 判断是否允许重新加入 | 一次成功后的永久健康 |
推荐采用“连续 3 次失败才摘除、连续 2 次成功才恢复”的滞后策略。没有滞后时,节点在可用与不可用之间反复跳转,用户会遇到连接重置、登录会话中断和 DNS 请求忽快忽慢。不同节点的评分也不应只看最低延迟,可以使用加权公式,例如将成功率占 60%、中位延迟占 25%、最近失败惩罚占 15%,再设置一个最低成功率门槛。
四、动手实现测速、筛选与动态切换
-
读取节点清单
脚本读取权限为 600 的节点 JSON,检查标签唯一性、地址格式、端口范围和必需协议字段。任何节点字段缺失时先跳过,并写入结构化日志。
-
建立 API 连接
连接
127.0.0.1:10085,设置 3 秒 gRPC 连接超时。连接失败时不要立刻重启 Xray,应先检查核心进程、监听端口和 systemd 日志。 -
逐个健康检查
按顺序测试候选节点,每个请求超时设为 5 秒,单轮测试之间可间隔 100 至 300 毫秒,避免节点数量较多时瞬间制造大量连接。
-
计算候选排名
剔除连续失败达到 3 次的节点,再按成功率、延迟中位数和最近一次成功时间排序。当前活动节点未明显劣化时,设置 10% 至 20% 的切换优势门槛。
-
执行出站切换
通过 HandlerService 增加目标出站、移除旧出站,或调用当前版本支持的路由覆盖能力切换 balancer 目标。执行后再次读取运行状态确认标签已生效。
-
写入结果日志
记录时间、旧标签、新标签、测试耗时、失败原因和 API 返回结果。至少保留最近 7 天日志,便于判断切换是否集中发生在某个时间段。
在实现 HandlerService 调用时,不要把“删除旧节点”和“增加新节点”写成没有保护的两个独立动作。脚本应先确认新节点健康,再调用增加操作;如果增加成功,再处理旧节点。遇到 API 超时,应重新查询当前出站列表,不能根据本地变量直接重复添加,否则可能产生重复标签或状态不一致。
# 伪代码:展示控制顺序,不是可直接执行的客户端命令
for node in candidates:
result = health_check(node, timeout=5)
update_score(node, result)
best = choose_best_node(min_success=2)
if best.tag != current_tag and better_than_current(best, margin=0.15):
api.add_outbound(best.config)
verify_outbound(api, best.tag)
api.switch_route(best.tag)
wait(60)
api.remove_outbound(current_tag)
write_event(current_tag, best.tag, result)
不同 Xray-core 版本对路由服务和 balancer 覆盖命令的支持细节可能不同。若当前版本没有可靠的动态路由切换接口,不要伪造一个不存在的 gRPC 方法;可以让脚本生成完整 JSON,先运行配置测试,再通过 systemd 重启核心。对生产环境而言,能回滚、可审计通常比少几秒中断更重要。
结论:先确认切换能力,再决定 API 方案
如果只能稳定调用 HandlerService,建议把节点生命周期控制在“加入、验证、移除”范围内;如果版本同时支持可靠的路由或 balancer 覆盖,再使用固定出站池和活动目标切换。不要因为文档中出现 API 服务名称,就假定所有版本都支持同一组请求字段。
五、权限、故障处理与 Linux 长期运行
运行脚本的系统用户不应拥有整个应用目录的写权限。可以建立专用用户,例如 xray-automation,只允许读取节点模板、写入状态目录和连接本机 API。节点 JSON 中可能包含 UUID、密码或 Reality 密钥,文件权限建议设为 600,父目录设为 700,日志中不得打印完整订阅地址、UUID 或私钥。
文件权限
- 节点配置
- root:xray-automation
- 权限
- 600
- 状态目录
- 700
- API 监听
- 127.0.0.1:10085
脚本只读取必要配置,敏感字段通过环境变量或受限文件提供。
运行策略
- 检查周期
- 60 至 180 秒
- 单次超时
- 5 秒
- 失败阈值
- 连续 3 次
- 重启策略
- 失败后指数退避
避免每次测速异常都重启核心,先区分节点故障和 API 故障。
systemd 服务可以使用 Restart=on-failure,但不要设置过短的重启间隔。建议使用 RestartSec=10,并为脚本增加单实例锁,防止上一次检查未结束时下一次任务重复修改出站。部署后先执行一次前台运行,确认 API 连接、健康检查、日志和切换动作全部正常,再交给 systemd 托管。
[Unit]
Description=Xray node selector
After=network-online.target xray.service
Wants=network-online.target
[Service]
Type=simple
User=xray-automation
ExecStart=/usr/local/bin/xray-node-selector --config /etc/xray/nodes.json
Restart=on-failure
RestartSec=10
NoNewPrivileges=true
PrivateTmp=true
[Install]
WantedBy=multi-user.target
报错:rpc error: code = Unavailable
原因与解法:API 端口未监听、核心已退出或脚本连接了错误地址——先执行 ss -lntp | grep 10085,再检查 Xray 服务日志和脚本中的端口。
报错:address already in use
原因与解法:旧出站或旧脚本实例仍占用本地测试端口——使用单实例锁,清理残留进程,并为每个临时测试入口分配唯一端口。
报错:failed to add outbound
原因与解法:节点 JSON 字段与当前核心版本不兼容,或标签已存在——先用相同核心执行配置校验,检查标签唯一性和协议字段类型。
报错:health check timeout
原因与解法:节点、目标站或服务器出口存在超时,不一定是 API 故障——分别测试其他节点,并记录 TCP、TLS 和 HTTP 阶段耗时后再决定是否摘除。
只调用 StatsService 能自动选出最快节点吗?
不能。StatsService 主要提供流量与计数信息,不能替代真实代理请求。应结合经节点转发的 HTTP 健康检查,并用连续结果计算排名。
健康检查每分钟执行一次会不会太频繁?
对少量节点和小响应地址通常可以,但节点超过 50 个时应分批检查,并把周期调整到 120 至 180 秒,避免产生不必要的连接和日志。
切换后旧连接会立即断开吗?
取决于实现方式。新请求可以走新出站,但已经建立的连接可能仍由旧出站维持;若直接移除旧出站,长连接可能被中断,因此应先设置冷却时间。
API 是否应该开放给远程监控服务器?
默认不建议。优先让监控程序通过本机代理、受限 SSH 隧道或专用安全通道访问;不要把未经认证的 Xray API 端口直接映射到公网。
完成部署后,至少连续观察 24 小时:记录每次切换的原因、节点成功率、平均延迟和切换后的恢复情况。如果切换次数异常高,先提高冷却时间和切换优势门槛;如果所有节点同时失败,则应检查服务器出口、DNS、目标测试站和 Xray 核心状态,而不是继续增加节点。真正可靠的自动选优系统不是“越快切换越好”,而是在可验证、可回滚和最少干扰业务之间取得平衡。