mzvideo 访问故障与性能排查复盘 (2026-07-26)
一句话:站点完全打不开 → 502 → 恢复但慢到 5 秒。根因是两起独立故障叠加 + 一个架构性性能瓶颈,排查过程本身比结论更值得借鉴。
一、概览
| 项 | 内容 |
|---|---|
| 受影响站点 | https://mzvideo.898311.xyz/ (个人 Quartz 知识库) |
| 故障时间 | 2026-07-26 下午 |
| 症状演变 | 完全不可访问 → HTTP 502 → 恢复 200 但响应 1.8~5s |
| 根因 | ① tx2 服务器假死 ② 本机 Quartz 构建死循环 ③ 静态站经 frp+Node 绕路 |
| 最终状态 | 服务恢复 200,性能问题待架构优化 |
二、架构拓扑(理解一切的前提)
公网用户
│ DNS: mzvideo.898311.xyz → 49.233.210.139 (tx2, 腾讯云北京)
▼
tx2 (49.233.210.139, 3.4G 内存)
│ nginx :443 (TLS 终止)
│ proxy_pass → 127.0.0.1:15000
▼
tx2 frps :15000 ◄── 隧道 bj-5000 的 remotePort
│
│ ═══ 公网 frp 隧道 ═══
▼
本机 frpc (192.168.3.104, 家用宽带)
│ localPort 5000 ↔ remotePort 15000
▼
本机 127.0.0.1:5000
│ quartz.service: npm run dev = npx quartz build --serve --port 5000 (Node)
▼
Quartz 静态站点
关键事实:
- mzvideo 的真实后端是本机笔记本上的 Quartz,不是某台公网服务器。
- 公网入口 tx2 只做 nginx 反代 + frps 隧道中继,真正的页面生成在本机 Node 进程里。
- 本机关机/重启/build 失败,mzvideo 立刻挂——这是一个强依赖本地开发机的脆弱架构。
三、故障一:tx2 整机假死(完全打不开)
现象
ping通(0% 丢包,10ms)- TCP 握手 22/80 大概率通、6900 时通时不通(25% 失败)
- 但所有应用层全超时:SSH
banner exchange超时、frps 连不上、HTTPS 443 不通
根因(journal 铁证,多重压力叠加)
上次 boot(7/14–7/26 18:38 假死)的死亡顺序:
image_upload.service内存泄漏(tx2 上一个 python 图片上传服务):7/26 01:15 其进程 pid 2950 RSS 飙到 1.86GB / 虚拟内存 2.9GB,触发全局 OOM,内核oom-killer将其 kill。- tx2 内存 3.3G + 完全无 swap(
Swap: 0B),没有任何缓冲,一个吃内存的进程就能把整机逼到 OOM。 - 7/26 15:23 frps panic 崩溃:
send on closed channel的 Go panic,frps 进程直接挂掉,frp 隧道服务中断。 - 同期 sshd 收到 71733 次暴破连接,叠加冲击。
- 结果:sshd/frps/nginx 等用户态进程先后崩溃或调度不上 CPU,内核仍能应答 ICMP/TCP 握手,但应用层全部超时 → 假死。
真正元凶是
image_upload.service内存泄漏 + 无 swap;71733 次暴破只是放大器。systemd-udevd Worker terminated by signal 9、腾讯云 stargate agent 反复重启都是 OOM 期的连带表现。
解决
腾讯云控制台强制重启 tx2。
教训
- “ICMP 通 + TCP 握手通 + 应用层全超时” = 服务器假死,不是网络断,也不是封禁(封禁会连 ping 一起断)。
- 公网 22 是暴破磁石,必须 fail2ban / 禁密码登录 / 限源 IP(端口可保留,但要有自动封锁)。
- 小内存机器别堆公网服务,配内存/CPU 告警在假死前预警。
四、故障二:mzvideo 502(tx2 恢复后)
现象
tx2 重启后返回 HTTP 502 Bad Gateway:
- tx2 nginx error.log:
upstream prematurely closed ... 127.0.0.1:15000 - 本机 frpc 日志:
[bj-5000] connect to local service [127.0.0.1:5000] error: connection refused
排查链路(分层定位法)
- DNS → 49.233.210.139 ✓
- tx2 nginx error.log → upstream 是
127.0.0.1:15000 - 本机 frpc 配置 →
bj-5000隧道 = 本机:5000 ↔ tx2:15000 - 本机:5000 无监听 →
quartz.service状态activating (auto-restart) journalctl -u quartz→npm run dev反复Main process exited status=1- 手动
npx quartz build→ frontmatter YAML 错误 - 全库检索 → 8 个文件 tags 含
@
根因
8 篇 md 的 frontmatter:
tags: [车设/语音, ..., @VoiceSearchProvider, ..., @Module(TAG_DRIVING)]YAML 中 @ 是保留字符,flow 序列 [...] 里裸写 @token 非法,frontmatter 解析抛异常 → quartz build 崩溃退出 → systemd Restart=on-failure 又拉起 → 死循环重启,5000 永远没服务 → frp 回源 refused → 502。
涉及文件(全博客检索结果):
车技车设/03-路由框架详解.md(@RouterProvider)车技车设/04-语音搜索插件详解.md(@VoiceSearchProvider)车技车设/整体架构探索/05-路由与语音搜索框架.md(@RouterProvider, @VoiceSearchProvider)车技车设/整体架构探索/模块详解/{Lock,Light,VehicleBody,Driving}-*/01-*.mdai-engineering/practice/08-可迭代知识库的工程设计.md
解决
Python 脚本清除 tags 中所有 @(并去掉残留引号)→ npx quartz build 通过 → systemctl restart quartz → mzvideo 返回 200。
踩坑记录(很重要)
批量改这批中文路径 md 时,常规工具在本环境接连失效:
find -exec sed {} +—— 没改到文件(疑似-exec {} +对中文长路径传参问题)bash globstar content/**/*.md——**未递归,只匹配到 1 个文件grep -rlE '^tags:.*@'—— 返回空(ugrep 对该组合 pattern 行为异常)
最终用 Python glob(recursive=True) + re 直接读写文件解决。
教训:批量修改中文路径文件,Python 远比 sed/find/globstar 可靠;不要假设熟悉的 shell 命令在所有环境行为一致。
五、性能问题:响应慢(1.8~5s)
mzvideo 恢复后响应极慢,用 curl -w 分解各阶段耗时定位瓶颈:
| 链路段 | 耗时 / 速率 | 判定 |
|---|---|---|
| 本机 quartz 处理 | 4ms | ✅ |
| 本机回环传输 | 21 MB/s | ✅ |
| frp 隧道(本机↔tx2) | 250ms / 63KB ≈ 2Mbps | ✅ 可接受 |
| tx2 → 公网用户 | 1.8s / 63KB ≈ 285kbps(抖动到 5s) | ❌ 瓶颈 |
瓶颈定位
- 首页 HTML 仅 63KB,公网下载却要 1.8s,实测速率 ~285 kbps,且抖动到 5s。
- 本机、frp 隧道都快;慢点 100% 在 tx2 → 公网用户的下行链路。
- tx2 刚从攻击+假死恢复(28 分钟前),公网网络不健康(疑似腾讯云限速/清洗或链路丢包)。
架构性问题(更根本)
静态站点本不该这样部署。当前每个请求要走:用户 → tx2 nginx → frp公网隧道 → 本机 Node serve → 原路返回,三段绕行,且:
- 本机负载
load 4.28、quartz 进程占 63% CPU,本机一抖就拖垮站点 - 家用宽带上行 + frp 隧道开销
- Node dev-server 本就不是生产级服务
优化方案
治标(保持架构):
- tx2 nginx 开 gzip/brotli(63KB HTML → ~15KB,传输快 4 倍)
- nginx 启用 HTTP/2 + upstream keepalive
- frp 开
tcpMux+ 增大poolCount,减少新建连接开销 - 查 tx2 是否被腾讯云限速/清洗
治本(强烈推荐)——Quartz 静态部署:
quartz build → public/ 纯静态文件 → rsync 到 tx2 → tx2 nginx 直接 serve
收益:
- 响应 <100ms(只剩 tx2 带宽这一关)
- 本机关机/重启/build 失败都不影响 mzvideo(彻底消除本次故障的根因)
- 去掉 frp 隧道 + Node serve 两层开销
- 配 git hook:push 后自动
build + rsync,保持实时性
六、经验与方法论(可借鉴)
6.1 分层排查法
网络问题从下往上逐层验证,每层用最小工具:
dig(DNS) → ping(ICMP) → </dev/tcp/host/port>(TCP 握手) → curl -v(应用层) → 业务日志。
永远先量化每一跳,不要靠猜。
6.2 “ICMP通 + TCP握手通 + 应用层全超时” = 服务端假死
内核在线但用户态进程卡死(OOM/过载)。重启可恢复,但要查 OOM 根因防复发。不是封禁(封禁 ping 一起断)。
6.3 frp 内网穿透排障套路
- 看本机 frpc 日志:能否连 frps(bind 端口)、隧道是否
start proxy success - 看 frp-panel 控制面:能否拉到配置(认证握手是否成功)
- frp 链路问题典型表现:502 / connection refused / prematurely closed
- 关键区分:frps 服务端挂(连不上 bind 端口) vs 本机后端没起(连得上 frps 但 localPort refused)——后者日志会明确报
connect to local service ... refused
6.4 systemd 死循环重启
Restart=on-failure + 内容错误 = 无限重启空耗 CPU。务必加:
StartLimitIntervalSec=300
StartLimitBurst=3看 systemctl status + journalctl -u <svc> 定位退出码和原因。
6.5 frontmatter / YAML 规范
- tag 不能用
@开头(YAML 保留字符),也不建议含#、!、:、{}、[]等保留符 - 含特殊字符的 tag 用引号
"..."或 block style - 一个坏 frontmatter 会拖垮整站构建——提交前务必
npx quartz build校验,建议加 pre-commit hook
6.6 静态站部署铁律
- 静态产物用 nginx 直接 serve,不要用 Node dev-server 暴露到公网
- 不要经 frp 反向代理(家用宽带上行 + 隧道开销 + 本机依赖)
- 公网服务器直接托管静态文件:最快、最稳、不依赖开发机在线
6.7 工具可靠性备忘
- 批量改中文路径文件:Python > sed/find/globstar
- 定位慢点:
curl -w '...%{time_starttransfer}...'分解 TTFB / total - 不要轻信”首次慢是正常”,量化每个阶段;静态站正常应 <500ms
七、待办清单
-
quartz.service加StartLimitIntervalSec=300/StartLimitBurst=3✅ (2026-07-26) - 🔴 tx2 加 4G swap ✅ (2026-07-27) —— OOM 有缓冲,不再零容错假死
- 🔴
image_upload.service加MemoryMax=1024M✅ (2026-07-27) —— 泄漏超 1G 只杀自身(systemd 自动重启),不再触发全局 OOM 蔓延 - 🟠 根治
image_uploadFlask app 内存泄漏(/opt/image_upload/app.py,限流是兜底,代码层才是根因) - 🟠 查 frps
send on closed channelpanic(7/26 15:23 崩溃),考虑升级 frps 或加自动重启 - tx2 装 fail2ban(22 保留供本机访问,但自动封暴破 IP)
- tx2 配内存/CPU 告警,OOM 前预警
- mzvideo 改静态部署到 tx2(根治响应慢 + 不依赖本机)
- content 加 pre-commit hook 跑
npx quartz build校验 frontmatter - 短期:tx2 nginx 开 gzip + HTTP/2