工作记录 · 2026年8月25日
最近五天的工作:让系统更轻、更稳、更可验证
从博客音频优化、监控刷新雪崩,到正式 Mod 数据包和 502 网关诊断,记录几次围绕性能、边界与验证展开的工程实践。

过去五天做的事情分散在几个项目里:继续完善这个博客、处理一套监控面板的性能问题、把一个网页游戏原型转换成正式 Mod 数据包,以及排查一次 AI 客户端持续重连的 502 故障。
它们看起来没有直接联系,但最后都指向了相似的问题:系统能运行只是起点,真正值得投入时间的是让它更轻、更稳定,也更容易证明自己没有出错。
本文已经省略内部地址、凭据、私有仓库和业务环境信息,只保留可复用的工程过程。
一、博客:功能增加之后,重新检查加载成本
这几天给博客补充了友链,也逐步完善了左下角的音乐播放器。播放器现在支持音量调节、静音状态、播放进度显示和拖动跳转,歌曲列表也加入了新的本地音频。
功能完成后,加载速度开始变得值得关注。检查 public 目录时发现,其中一首 FLAC 音频约 47 MB,远大于站内所有图片之和。即使 <audio> 只预加载元数据,不同浏览器和 CDN 对 Range 请求的处理仍可能产生额外流量;而且播放器挂在全站布局上,每个页面都会创建音频元素。
最后做了两层处理:
- 将 FLAC 转成 192 kbps MP3;
- 把播放器的
preload从metadata改为none,只在用户点击播放后请求音频。
转换后文件从约 47 MB 降到约 10.7 MB,减少约 77%。转换不是只看文件是否生成,还对整首 MP3 做了完整解码校验,确认 7 分 25 秒的音频没有中断,采样率和声道保持正常,再替换播放列表并删除仓库中的 FLAC。
这次调整提醒我:一个小功能的代码可能只有几十行,但它引用的资源才是真正的性能成本。以后博客音频默认使用压缩后的 MP3,并把“可播放”和“不会拖慢首屏”视为同一项验收标准。
二、监控:五秒刷新如何变成请求雪崩
另一项工作来自一套 Prometheus 与 Grafana 监控页面。最初的现象是“单次刷新越来越慢”,直觉上很容易怀疑探头、网络或 PromQL 查询变复杂了。
排查时先把链路拆开:浏览器、Grafana、Prometheus、Exporter 分别计时。仪表盘大约包含 20 个面板和 30 多条查询,直接请求 Prometheus 时,全部查询总耗时仍不到一秒,单条查询也没有明显异常。这说明数据源不是主要瓶颈。
Grafana 一侧则出现了完全不同的情况:容器内存接近上限,鉴权和仪表盘索引操作出现明显延迟,一次页面请求可能持续几十秒。与此同时,前端仍按 5 秒间隔自动刷新。
最终形成了一个自我强化的循环:
5 秒触发下一轮刷新
-> 上一轮仍未结束
-> 请求开始重叠
-> 内存和鉴权压力继续增加
-> Grafana 响应更慢
-> 浏览器取消旧请求
-> 日志出现大量 499
这里最关键的判断是:刷新周期必须大于系统在压力状态下完成一轮工作的时间。否则自动刷新不是提高实时性,而是在主动制造积压。
处理时把两个入口统一改为一分钟:页面生成的 iframe URL 使用 refresh=1m,Grafana Dashboard 自身也设置为 1m。如果只修改其中一处,另一处仍可能覆盖最终行为。修改后通过前端测试、生产构建和 Grafana API 返回值确认配置真正生效。
长期方案还包括增加 Grafana 资源余量、缩短默认时间范围,以及继续检查匿名鉴权和索引层延迟。但仅把刷新频率从“远小于响应时间”调整到合理范围,就先终止了请求雪崩。
三、从网页原型到正式 TFM2 Mod
最近还在做一个以团队竞技角色为主题的管理游戏原型。项目最初是独立网页,随后确认 Teamfight Manager 2 正式支持 Steam Workshop,并公开了数据英雄、数据库包和扩展 API,于是目标从“模拟相似玩法”改成“生成游戏能够直接识别的正式包”。
第一步不是写转换脚本,而是确认官方格式的能力边界:
.data_champion可以描述英雄属性与原生战斗效果;- 数据库包可以替换战队、选手与赛事数据;
- 自定义逻辑可以进一步使用稳定 API,但会增加编译和平台依赖。
第一版优先选择纯数据方案,不引入原生 DLL。53 名英雄被拆成两个可独立启用的包:40 名核心英雄,以及依赖核心包的 13 名赛季扩展。这样保留了赛季英雄池切换,同时降低安装和兼容成本。
职业数据库部分比英雄 JSON 更复杂。官方文档说明了打包方式,却没有完整公开二进制字段结构,因此构建过程需要验证数据库头、导入类型、Gzip 长度和 CRC,而不是简单地修改扩展名。
最终生成内容包括:
- 53 个英雄行为文件;
- 中英文文本与本地图片资源;
- 120 支战队和 910 名选手槽位;
- 2,751 项随机新秀姓名池;
- 可重复执行的构建与校验脚本。
内容审计时曾发现一个容易遗漏的问题:正式队伍和现役选手已经全部替换,但基底数据库还保留了其他项目的随机新秀姓名池。它不会影响开局,却会在未来赛季生成不属于当前主题的名字。定位到对应向量边界后,构建脚本也对这一部分完成替换,并把数量与内容检查加入回归验证。
完整校验覆盖英雄 schema、资源引用、PNG 可解析性、包依赖、数据库 Header、Gzip、CRC、队伍和选手映射。由于本机没有安装 TFM2,目前仍缺少游戏内 Diagnostics 和实际比赛运行测试。这项限制被明确保留在交付说明里,而不是用“格式校验通过”代替“运行验证通过”。
四、502 重连:健康检查通过不代表业务接口可用
还排查了一次 AI 客户端持续显示 Server is busy 并自动重试的问题。错误表面上是 502,看起来像一次普通的临时网络波动,但客户端在多次重连后仍然失败。
诊断时分别检查了本地配置、客户端日志、网关健康接口、消息路由和模型可用性。结果是:网关首页和 /health 可以返回 200,但真正的 Messages 路由仍返回 502 或 404;同时客户端默认选择的高负载模型持续收到 busy,几个计划作为 fallback 的模型名又不在当前账号的可用列表里。
这说明故障同时存在三个不同层次:
- 进程与网络是否可达;
- API 路由是否真实提供服务;
- 当前凭据是否有权调用指定模型。
只验证 /health 会得到“网关正常”的错误结论,只重启客户端也无法修复上游路由。最终能够确认的是本机配置格式和客户端进程没有结构性问题,主要故障位于第三方网关或上游模型服务;默认模型可以调整到更稳定的候选项,但在网关实际支持之前,不能把这次工作描述成已经彻底修复。
这次排查留下的另一个原则是:任何曾出现在本地日志或配置中的 API Token,都应该在诊断结束后轮换。日志是排障证据,也可能成为新的暴露面。
五、这一阶段的共同方法
回看这些工作,真正反复出现的是同一套方法。
先拆开链路
页面慢不一定是图片,监控慢不一定是 Prometheus,502 也不一定是客户端。把系统拆成资源、浏览器、服务端、数据源和上游,再分别测量,能避免在错误层级反复修改。
区分“格式正确”和“运行正确”
MP3 能完整解码、数据库 CRC 正确、生产构建通过,都是必要证据,但它们不能互相替代。没有游戏运行环境,就应该明确写出尚未完成运行时验证。
为隐蔽数据建立校验
播放器里的大文件、数据库里的随机姓名池、Dashboard 的第二个刷新入口,都不是主流程中最显眼的部分,却会在后续产生真实问题。发现一次之后,最好把它变成脚本或测试里的永久规则,而不是只做一次人工修补。
性能优化先减少不必要的工作
把 47 MB 音频压缩并延迟加载,比微调几行 React 更直接;把刷新周期调到一分钟,比在请求重叠时继续优化单条 PromQL 更有效。很多性能问题的第一步不是“做得更快”,而是“不要做得这么频繁”。
这一阶段完成了几个具体功能,也留下了一些尚未结束的验证项。对我来说,这比一份全部标成完成的清单更有价值:工作记录不只是展示结果,也应该保留判断发生变化的原因,以及哪些结论仍然需要下一次实践来证明。