liuzhuohua
|
ed5eb4428b
|
1.0.9 静态资源传输层优化 + 页脚版本号 + logo 回概述
## 性能:瓶颈在传输,不在代码
先量后改。helper 冷启动 0.44s(编译 22173 行)看着像瓶颈,但
drouter-helpd 是常驻守护,请求走 Unix socket,正常路径根本不 fork;
实测各 API 已是毫秒级。app.js 语法编译 18ms、顶层执行 18ms
(393 个顶层函数 / 13401 行)—— JS 执行不是瓶颈。
真瓶颈是 app.js 690,482 字节每次重传。**决定不拆包**:JS 执行只 18ms,
拆包引入首屏 waterfall 的风险大于收益。改走三条:
1. 缓存策略按「有没有指纹」区分(不是按扩展名)
- 带 ?v=<mtime> → public, max-age=31536000, immutable
- 没指纹 → 仍 no-cache(否则部署后用户拿到旧文件)
- index.html → 永远 no-store(每次都要重新注入)
2. 编码协商 br > gzip > 原文,**逐级降级**
3. 压缩结果按 (path, mtime, size, enc) 缓存
真机实测:首次 690,482 → 200,137 字节,第二次打开 0 字节(304)。
## 逐级降级不是可选项
目标机**没装 python3-brotli**(只有 C 库 libbrotli1)。最初写的
_brotli_cached 在模块缺失时 `return data, None`,而 None 是 encoding,
调用方理解为「压不动,原样输出」—— 于是 583KB 裸传,**比优化前更慢**。
改成 (None, None) 明确表示「这条路不通」,由 _encode_body 继续试 gzip。
同时修:Accept-Encoding 探测必须认 q=0(br;q=0 是「明确不要」,
子串匹配会正好发给解不开的客户端 → 满屏乱码);编码必须进缓存键
(共用键会互相覆盖);brotli 固定 q5(q11 只省 10KB 却要 1.5-3s)。
## 宽 except 把 bug 装成了特性
兜底只接 ImportError 这类**真·预期**的缺失,不接 TypeError/ValueError
(「str 传给要 bytes 的接口」也是 TypeError,接了等于没接)。
早先的 `except Exception` 吞掉了一个 NameError(漏注入 _comp_lock),
表现是压缩功能完好但从不生效,页面照常能开、只看响应头的判据完全发现不了。
## 版本号:建立单一真源 packaging/VERSION
原来版本号在 4 个构建脚本各写死一个默认值 + openapi 里硬编码。
漏改一处就产出「文件名 1.0.9、control 里 1.0.8」的包 —— apt 按
control 认版本,dpkg -l 永远显示旧版且升不上去。
index.html 的 ETag 并入版本号:它发出去的是注入后的内容,
ETag 却按原文件算,只改 VERSION 文件时 ETag 不变,客户端换到 304
继续用旧副本,页面版本号永远停在旧版。
## logo 回概述
用 <a> 而不是 <div onclick>:Tab 能聚焦、Enter 能触发、读屏会念「链接」。
已在 dash 页时不重复 go()(go 会 stopPageTimers + 重拉数据)。
CSS 显式去 text-decoration/color(UA 样式表不声明仍生效),
:focus-visible 焦点环保留(纯键盘用户唯一的路标)。
## 顺带修掉一个已发布版本的真 bug
_vpn_env 里 os.path.join('/lib/modules', ...) 被留成了 '/nonexistent'
(反向验证脚本被 SIGTERM 打断,finally 没执行,注入留在源码里),
于是 mod_file 永远为空、**need_module 整个状态从四态里消失** ——
这正是 v1.0.8 release notes 声称修好的那个 bug(PVE 的 KVM 虚机内核
明明有 wireguard.ko.xz 却报 unsupported,前端配一句写死的
「通常说明跑在精简容器里」把排查方向带偏)。
新增 _dev/t-worktree-clean.py 扫注入哨兵,放在 sync.sh 所有判据之前 ——
基线被污染时,后面每一条红都不可信。
## 判据
新增 t-static-perf.py(42 项静态)、t-encoding-runtime.py(26 项**真跑**,
覆盖 9 种 Accept-Encoding 组合、brotli 缺失降级、两种编码不互相覆盖、
压不动直出、内部错误不被吞)、t-worktree-clean.py。
三个都做了反向验证,注入后确实变红。
反向验证本身也踩了坑:判据里 len(body) 在 body 为 None 时先崩 ——
退出码非 0 但没有 FAIL 行,看着像「跑了」,实际没人知道它验出了什么。
取长度一律过 _sz() 包一层。
全量回归 54 个判据 0 失败;真机验收 22 项全通过;
前端三查 47 视图 0 异常、26 项移动端适配全过。
|
2026-10-04 10:27:02 +08:00 |
|
liuzhuohua
|
2859705cee
|
fix(webshell): 修复终端满员自锁 + 去掉滚动条并加清屏按钮
一、修 PTY 不可用(Web 终端报「创建终端失败:out of pty devices」)
根因是目标机上 /dev/ptmx 这个符号链接丢了(devpts 里 /dev/pts/ptmx
还在,但入口没了),于是 pty.openpty() 抛出误导性的「out of pty devices」。
- 新增 ensure_ptmx():探测 -> ln -sf pts/ptmx /dev/ptmx -> mknod 兜底
- 新增 _open_pty():首次失败先自愈再重试一次,仍失败就把真实原因
(_pty_diagnose() 的结论)包进异常,不再只留一句天书
- 服务启动时也自愈一次,开机即修,不等用户踩坑
- 前端识别 PTY_FAIL 并给出可照抄的修复命令
二、修会话满员时的死锁(连到第 9 个会话整个终端服务冻死)
connect 需要「看是否满员 -> 满了就回收 -> 再看一次」,这个序列必然
在持锁时调到内部也要拿锁的 _reap_idle()。而 _lock 是普通 Lock,
同线程二次 acquire 永久阻塞 —— 不报错、不超时,直接锁死;而且锁
一直不放,其他会话的 read/write 也一起堵住,整个 Web 终端报废。
- _lock 改为 RLock
- connect 不再把 _reap_idle() 放在 with _lock 里面
- _reap_idle() 锁内只做登记摘除,close() 移到锁外:
close() 要等子进程退出(SIGHUP 后最多轮询 3 秒),持锁做同样会
把并发请求堵死
三、终端外观(按需求)
- 去掉右侧滚动条(scrollbar-width:none + ::-webkit-scrollbar),
但保留滚动能力 —— 直接关掉 overflow 会让人再也翻不了历史
- 高度改为 clamp(260px,54vh,560px) 随视口自适应;内容多了自动跟随
最新输出。刻意不做「高度随内容增长」:wsMeasure() 用 box.h 算行数,
内容撑高容器会反过来改变 PTY 行数,形成反馈环
- 标题栏加「清屏」小按钮;滚上去离开底部时另出现「↓ 最新」——
滚动条没了,总得留个回到底部的入口
- 跟随阈值收敛成唯一的 WS_TAIL_GAP:wsAutoScroll 与 wsSyncTailBtn
各写一份会出现「按钮没出现、输出也不跟着滚了」的死角
四、顺带清理
- 删掉 app.css 里整套失效的旧终端样式(.tdot/.tt/.term-in/.tk-*,
app.js 中 0 引用),它们已被新结构完全覆盖,留着只会让人改错地方
- 合并 .term-wrap / .term-body 各自重复的第二条声明
测试:
_dev/t-pty.py 扩到 37 项,含「不得自锁」的结构检查 + _reap_idle
行为验证(摘除/关闭/返回条数);_dev/t-webshell.py 加 16 项外观契约。
两者都挂进 devtools/sync.sh 预检链。
新增 devtools/preview-webshell.py:用真实 app.css + 从 viewWebShell()
抠出的 DOM 生成离线预览页,不手抄样式以免预览与线上不一致。
真机验收(192.168.7.3):
8 个会话满员后第 9 个 0.01s 干净返回 BUSY(修复前是永久卡死);
8 个会话 0.87s 全部断开、残留 0(修复前残留 8 个且后续全卡);
/dev/pts 与 pty/nr 回落到 0,无 fd 泄漏;root shell 正常执行命令。
|
2026-10-01 01:29:09 +08:00 |
|