搜索 K
Appearance
适用对象:用 Sub-Store(Docker / 宝塔 / Node 自托管 / Serverless 版)聚合多个机场订阅的进阶用户。 内容定位:不是"重装一遍就好了"的敷衍文,而是把采集层、处理层、渲染层三条链路的失效点逐个拆开,给出可复现的命令与判定阈值。
先把结论摆在最前面,能对上号就直接跳到对应章节:
137 = OOMKilled(内存超限或脚本死循环);143 = SIGTERM(被面板或守护进程强杀);1 = 配置解析失败;0 但立刻退出 = 端口占用或环境变量缺失。要排障,先得知道数据在哪儿断的。Sub-Store 的运行模型可以粗暴切成三层:
采集层(Fetcher)。 按照你配置的订阅 URL 列表,逐个发起 HTTP(S) 请求,拿到 Clash YAML、base64 通用订阅或 V2Ray 订阅原文。这一步的瓶颈全在网络:DNS 解析耗时、TLS 握手往返、跨境 RTT、上游 CDN 回源、以及机场侧的 QPS 限流。如果 Sub-Store 部署在境内 VPS,它拉取境外机场域名时走的是普通公网路径,碰上台风天(运营商国际出口拥塞)就是成片 timeout;部署在境外 VPS 反而更稳,因为同区域回源。
处理层(Parser & Script)。 把多份订阅解析成节点对象数组,执行你写的 JS 脚本做过滤、重命名、去重、测速排序、按地区分组。这里是纯 CPU + 内存操作,与网络无关。语法树解析失败抛 SyntaxError,正则回溯爆炸或死循环导致 CPU 打满、内存暴涨,最终被内核 OOM Killer 干掉(对应退出码 137)。很多用户以为"容器挂了是网络问题",其实是自己从网上抄来的一段正则写错了。
渲染层(Renderer)。 把处理后的节点数组渲染成目标客户端格式(Mihomo/Clash Meta、Sing-box、Surge、Loon),写入本地缓存或推送到 Gist、私有 Git 仓库、对象存储。这一步的失效点通常是 Token 过期、Gist API 限流(未认证请求每小时 60 次)、磁盘写满。
再说物理层。2026 年主流优质机场的线路已经明显分层:IEPL/IPLC 是内网专线,用户侧数据在境内入口落地后走运营商内网直达境外 POP,不经过公网国际出口,丢包和抖动极低;BGP 中转是公网多线优化,看的是中转商的带宽储备;直连基本只在同区域(如日韩新)体验尚可。协议侧,BBRv3 拥塞控制在有丢包的跨境链路上比 CUBIC 提升明显,但前提是两端内核都支持且没被中间设备做 QoS 整形。TLS Reality 解决的是服务端证书特征问题,不解决"你的容器 DNS 被污染"这种客户端侧问题——这两件事经常被混为一谈。
关键认知:Sub-Store 本身不产生任何加速能力。它只是个配置装配厂。你聚合了 10 个机场,不会让速度变成 10 倍,只会让你的单点故障面积变成 10 倍。
下面这张表是排障时的"体检单"。取值逻辑:把 Sub-Store 部署在境外 VPS,订阅源为 8 个机场,节点总量约 600 条。
| # | 指标 | 健康基线 | 亚健康区间 | 危险阈值 | 采集命令 |
|---|---|---|---|---|---|
| 1 | 单机场订阅拉取耗时 | 200–800ms | 800ms–3s | > 5s 或直接 timeout | curl -w "%{time_total}" -o /dev/null -s URL |
| 2 | 聚合总耗时(8 源) | 3–10s | 10–30s | > 60s | 面板任务日志时间戳差 |
| 3 | 容器常驻内存 | 80–200MB | 200–500MB | > 800MB 触发 OOM 风险 | docker stats --no-stream |
| 4 | 脚本单次执行时长 | 50–300ms | 300ms–1.5s | > 3s 疑似正则回溯 | 脚本内 console.time |
| 5 | DNS 解析耗时 | 10–60ms | 60–200ms | > 500ms 或 NXDOMAIN | dig +short host @127.0.0.11 |
| 6 | TCP 443 握手 RTT | 30–120ms(专线) | 120–250ms(中转) | > 300ms 体验劣化 | tcping host 443 |
| 7 | 跨境链路丢包率 | 0% | 0–1% | > 3% 连接反复重传 | mtr -rwzc 50 host |
| 8 | Gist / 远端同步成功率 | 100% | 95–99% | < 90% Token 或限流问题 | 任务日志 HTTP 状态码 |
| 9 | 输出配置体积 | 0.5–2MB | 2–5MB | > 8MB 客户端解析卡顿 | curl -s URL | wc -c |
| 10 | 节点去重后保留率 | 85–100% | 60–85% | < 60% 上游污染严重 | 日志中节点数前后对比 |
注意:第 10 项经常被忽略。如果你的聚合结果里节点数比预期少了一大截,先检查是不是不同机场复用了同样的节点名称,被去重规则误杀了。
特征:docker ps 看不到容器,或状态反复 Restarting。
急救三步:
docker ps -a --format "table {{.Names}}\t{{.Status}}\t{{.RunningFor}}"
docker inspect --format='{{.State.ExitCode}} OOM={{.State.OOMKilled}}' sub-store
docker logs --tail 200 sub-store137 + OOM=true:调大 mem_limit,同时用二分法排查脚本里的死循环与超长正则。143:典型是宿主机内存吃紧时 systemd-oomd 或宝塔守护进程发的 SIGTERM。检查宿主 dmesg -T | tail -50。1 且日志停在配置读取:环境变量缺失、data 目录权限不对、或挂载卷是只读的。Restarting (0) 循环:端口冲突最常见,另一个是时区/主机名解析导致的启动脚本提前退出。特征:日志出现 SyntaxError: Unexpected token、Unexpected end of input、Invalid or unexpected token。
定位方法论——二分注释法:
反引号 未闭合、正则里出现未转义的斜杠、复制粘贴时带了不可见字符(BOM、零宽空格)。高频坑:从论坛复制的脚本里含全角逗号 ,;JSON.parse 接收了带尾逗号的字符串;Object.assign 里缺了 }。
特征:日志大量 ETIMEDOUT、ESOCKETTIMEDOUT、socket hang up、getaddrinfo ENOTFOUND。
分三种情况:
ENOTFOUND / EAI_AGAIN:DNS 层问题,容器内 DNS 未正确继承宿主,或上游域名被污染;ETIMEDOUT 握手后无响应:上游限流或国际出口拥塞;socket hang up:上游服务端主动中断,常见于机场对非客户端 UA 的拦截。修复顺序:先确认容器 DNS → 再确认 Sub-Store 自身出网代理 → 再确认机场订阅是否为"客户端专用 UA"链接 → 最后才考虑加大 timeout 并启用并发。
403 多为 UA 校验或 Referer 校验;429 是限流,通常因为你把同一个订阅同时在 Sub-Store、客户端、测速工具三处高频拉取。降低拉取频率(建议单机场不低于 6 小时一次)并开启本地缓存。
面板显示"同步成功"但客户端拉不到配置,八成是缓存层没刷新或者远端 Gist 有 CDN 缓存。用带随机参数的 URL 绕开缓存验证:curl -s "https://你的面板地址/api/你的Token?t=$(date +%s)" | head -20。
适合上 Sub-Store 的人:持有 3 个以上机场、需要统一节点命名规则、需要按地区/延迟自动分组、有轻量开发能力能看懂日志。
不适合的人:只用一个机场——聚合工具对你是纯负债;不具备任何命令行能力——出故障时你连日志都读不出来,直接断网。
折中方案:用支持"多订阅订阅组"的客户端(Mihomo 内核的 proxy-providers)做轻量聚合。它没有脚本层,不会出现语法报错,故障面小一个数量级。代价是不能做复杂的重命名和过滤。
Docker 部署的最小可用加固配置:
services:
sub-store:
image: xream/sub-store:latest
container_name: sub-store
restart: unless-stopped
mem_limit: 512m
memswap_limit: 512m
dns:
- 1.1.1.1
- 8.8.8.8
environment:
- SUB_STORE_FRONTEND_BACKEND_PATH=/你的随机路径
- TZ=Asia/Shanghai
ports:
- "127.0.0.1:3001:3001"
volumes:
- ./data:/opt/app/data要点解释:mem_limit 必须显式设置,否则 OOM 会由宿主内核随机决定杀谁;dns 显式指定避免继承宿主上有问题的解析器;端口只绑 127.0.0.1,通过反向代理暴露,别裸奔公网。
绝对不要做的事:让 Sub-Store 走"它自己生成的代理配置"出网。这构成自环依赖——一旦配置出错,它连上游订阅都拉不到,故障从"配置错误"升级为"完全瘫痪"。
客户端侧:Mihomo 系(Clash Verge Rev、ClashX Meta)建议开启 profile: store-selected: true,把手动选择持久化;Sing-box 用户注意 outbounds 里的 urltest 间隔不要低于 3 分钟,否则会持续向聚合端点发请求,反而拖慢面板。
在容器内部执行(docker exec -it sub-store sh):
# 1. DNS 是否正常
dig +short 上游域名 @127.0.0.11
nslookup 上游域名 1.1.1.1
# 2. 拉取耗时与状态码
curl -o /dev/null -s -w "code=%{http_code} dns=%{time_namelookup} conn=%{time_connect} tls=%{time_appconnect} total=%{time_total}\n" "订阅URL"
# 3. 强制 IPv4(排除 IPv6 黑洞)
curl -4 -o /dev/null -s -w "%{http_code} %{time_total}\n" "订阅URL"
# 4. 模拟客户端 UA
curl -A "clash-verge/v2.0" -o /dev/null -s -w "%{http_code}\n" "订阅URL"
# 5. TLS 握手细节
openssl s_client -connect 上游域名:443 -servername 上游域名 -brief在宿主机执行:
# 路径质量:丢包与逐跳
mtr -rwzc 50 上游域名
# TCP 端口可达性
tcping -c 5 上游域名 443
# 容器资源实时快照
docker stats --no-stream sub-store判定表:
| 现象 | 最可能原因 | 处置动作 |
|---|---|---|
time_namelookup 占 total 的 80% | 容器 DNS 异常 | 显式指定 dns: 并重启 |
code=429 | 上游限流 | 降低拉取频率 + 开启本地缓存 |
code=403 但浏览器能开 | UA / Referer 校验 | 换用客户端 UA 或申请专属订阅链接 |
curl -4 正常、默认异常 | IPv6 黑洞 | 容器内禁用 IPv6 |
mtr 第 5-8 跳开始丢包 | 国际出口拥塞 | 换用 IEPL/IPLC 线路的上游 |
| 握手正常但无响应体 | 中间设备 QoS 整形 | 换端口 / 换线路 / 开启 BBRv3 |
聚合失败时,很多人第一反应是"再买一家补上"。但如果你补的是坑,问题只会放大。
| 宣传话术 | 真实含义概率 | 验证方法 |
|---|---|---|
| "IEPL 专线,全程内网" | 约 40% 实为普通中转 | 看入口 IP 归属与 mtr 路径是否出现境外公网跳 |
| "无限流量不限速" | 通常隐含 FUP 软限制 | 连续跑量 2 小时后复测速率 |
| "解锁 Netflix 全区" | 多数仅解锁自制剧 |