Files
blog/诊断报告-2026-10-06.md
T
zqlit c2f890a0f7 feat: favicon 边缘缓存 + CDN 整站刷新兜底 + 评论孤儿修复归档
- blog-admin tools.ts: favicon 响应加 s-maxage/CDN-Cache-Control,
  让 CF 边缘可缓存,边缘命中即不进 Worker,KV 读取量大幅下降
- scripts/refresh_cdn.js: 裸域名自动补尾斜杠(多吉云 rtype=path 是目录刷新,
  https://usj.cc 只刷首页,https://usj.cc/ 才是全站),并打印刷新清单
- 新增文档:评论孤儿修复方案、函数版证书管家方案、诊断报告
2026-10-06 12:46:05 +08:00

14 KiB
Raw Blame History

四问诊断报告(2026-10-06)

一次性回答四个问题:CDN 刷新、KV 配额、D1 评论对账、certimate 迁移。


一、多吉云刷新 & 已删文章还能访问

1.1 先更正我自己的话

我一开始说「多吉云那条已经是全量(--all)」——这句话是错的,实测证据如下:

cnb-secrets.yml:33   CDN_URL_LIST: "https://usj.cc"

scripts/refresh_cdn.js 只做一件确定性的事:把 CDN_URL_LIST 按逗号/换行拆开, 逐条调 /cdn/refresh/add.json(rtype: "path")。当前配置里这个变量只有首页一个 URL。

结论:多吉云确实不是全量刷新,只刷新了首页 https://usj.cc。

1.2 那为什么删掉的文章还能访问?

不是多吉云的问题。实测这条 URL:

$ curl -sI https://usj.cc/202610021614.html
HTTP/1.1 200 OK
Server: marco/3.2                                  ← 又拍云特征头
X-Cache-Lookup: Hit From Upstream Cluster          ← 命中缓存
Last-Modified: Sun, 04 Oct 2026 14:12:26 GMT       ← 旧副本时间
Cache-Control: max-age=691200                      ← 8 天 TTL
Age: 131684                                        ← 已缓存约 1.5 天

判别实验(关键):

URL 状态 说明
/202610021614.html(已删除) 200 缓存里还有旧副本
/zzz-not-exist-111.html(从未存在) 302 → /404.html 源站没这个文件

两者行为不同 ⇒ 说明源站确实已经没有这个文件了(本地 content/ 和 public/ 均已确认无此页), 现在返回 200 完全是又拍云上的 8 天旧副本。

1.3 根因:两条 CDN 线路都刷不到「已删除页」

scripts/purge_list.js 的逻辑是:遍历 构建产物 public/,把里面所有 .html 列出来提交刷新。

// 全量时 walkHtml('public') —— 遍历构建产物
// 已删除的页面自然不在里面 → 永远不会被提交刷新

也就是说:刷新清单只会包含「现在存在的页面」。一个页面被删掉后, 它就从清单里消失了,于是 CDN 上的旧副本只能等 TTL 自然过期。

  • 又拍云:HTML max-age=691200 = 8 天
  • 多吉云:只刷首页,问题更明显

1.4 处置建议

方案 说明 成本
A. 什么都不做 等 8 天自然过期 0
B. 手动刷新一次(推荐) 把已知的已删 URL 提一次刷新,立刻生效 一次性
C. 流水线加「删除清单」 构建时对比上一次产物,把消失的 .html 追加进 purge 清单 改脚本

对已删文章这种低频事件,B 足够。需要的话我可以立刻把已知的那几个已删 URL 提交到又拍云+多吉云。


二、KV 配额告警:需要调整吗?

2.1 告警内容

已使用 Workers KV 免费套餐每日限制的 50%,超限将返回 429;重置时间 2026-10-06 00:00 UTC。 免费额度:读 100,000/天、写 1,000/天、删 1,000/天、list 1,000/天。

2.2 谁在吃配额?(实测 4 类来源)

来源 代码位置 频率 影响
评论数缓存 comments.ts:180/206 每次评论列表请求,miss 时写(TTL 600s) 读 + 写
评论数版本号 comments.ts:53/61 评论增删时写 写
friend-link favicon routes/rss/tools.ts:86 每次访问友链/友圈页,每个友链一次 读(大户)
人机验证 / 通知节流 human.ts / admin-notify.ts 低 少量

读数最大的就是 favicon 接口。 友链共 54 条(可见 42 条),其中 15 条头像走 /api/favicon:

// circles.html / linkify.js
img.src = RSS_API_BASE + '/api/favicon?url=' + encodeURIComponent(target);

每刷一次友圈/友链页 → 触发 N 次 /api/favicon → 每次至少 1 次 KV get。 favicon 本体有 24h 浏览器缓存,但首访、清缓存、换设备都会重新打。

2.3 建议:暂时不用调整,但建议做一个小优化

  • 读 50% 是「接近」不是「超了」,且每天 UTC 0 点清零。按当前站点的访问量,正常不会打穿。
  • 即使打穿,后果是 /api/favicon 走兜底字母图、评论数退化为实时查库——不是灾难性故障。
  • 免费版写限额 1,000/天,比读更容易被评论数缓存写穿。真正的风险点是评论突然暴涨或有人刷评论。

可做的小优化(成本低、收益直接):

  1. favicon 接口响应已经带了 Cache-Control: max-age=86400,可以加一层 Cloudflare Cache Rule, 让 /api/favicon* 在边缘缓存 24h,这样 KV 读基本归零。
  2. 若担心写限额,把 cml:ver: 的写入合并/延迟(比如 5 分钟内同页重复 bump 只写一次)。

要不要升级到付费($5/月)? 目前不需要。等到真收到「已超限」而不是「接近」的邮件再说。


三、D1 评论对账:老文章评论为什么没了

3.1 先说结论(这个和你想的不一样)

原库 3979 条评论 / 线上 3423 条。乍看差 556 条,但拆开看,「缺失」的绝大部分根本不是评论:

项 数量
线上缺失合计 711 条 / 63 页
其中 点赞型记录(内容含 [LIKE]) 494 条(69%)
→ 真实评论型缺失 217 条

关键发现:线上 [LIKE] 记录为 0 条。

$ SELECT SUM(CASE WHEN content LIKE '%[LIKE]%' THEN 1 ELSE 0 END) FROM comments;
→ 0

说明当年数据导入时,已经主动过滤掉了所有点赞型记录——这是正确的设计(点赞不该占用评论表)。 所以拿原库和线上直接比条数,本身就会假性多出 494 条。

3.2 缺失的 217 条真实评论,按站点拆

站 缺失 其中点赞 真实
伍比贰 521 431 90
优世界 101 0 101
演示站 48 41 7
往后 41 22 19

注意:「伍比贰」「演示站」「往后」是另外的站点,不属于 usj.cc。 它们的数据本来就不该进本站。

3.3 「优世界」那 101 条对得上现站吗?

逐页核对,结果是 一页都对不上:

page_key 条数 现站情况
/yl 48 页面不存在(是 2022 年的旧友链页)
/20241018-some-useful-win-tools 12 文章还在,但 slug 已改为 20241019,且是草稿(未发布)
/27、/13、/m/17 等 41 不存在(旧站的分页/短链)
/20240921-clock-in 5 文章已删除

根因不是「key 对不上」,而是这些文章本身已经不存在了。 评论是挂在「已删除文章」上的,key 再怎么规范化也接不回来。

3.4 站上现存页面 vs 线上 —— 有没有真的错位?

没有。 用现站 284 个 slug 逐一匹配线上缺失的 63 个 key:

能对上现存页面: 1 页 / 4 条   (只有 /about.html)
对不上          : 62 页 / 707 条

唯一一个疑似错位:/about.html(4 条)——但深挖后发现它也不需要修:

id=14229  /about.html  [伍比贰]  2026-02-07  👍 已点赞 Cool [LIKE]
id=14268  /about.html  [伍比贰]  2026-02-12  👍 已点赞 Interesting [LIKE]
id=14269  /about.html  [伍比贰]  2026-02-12  👍 已点赞 很棒的文章! [LIKE]
id=14276  /about.html  [伍比贰]  2026-02-13  👍 已点赞 写得很好 [LIKE]

这 4 条全是点赞型记录,而且属于**「伍比贰」站**,本就不该进本站。

→ 结论:线上 D1 没有任何错位,完全不需要修复。

3.5 线上「多出」的 7 页是什么?

page_key 条数
/20260630162329.html 25
/20260724101237.html 24
/20211001.html 14
/20261005113439.html 13
/20260629223659.html 10
/202610011226.html 2
/20261005213010.html 2

这些是导出 db 之后新增的评论(2026-06 之后),属于正常增长,不用动。

3.6 修复建议

结论:线上 D1 不需要做任何修复。

你的直觉「很多评论没跟页面 key 对应上、老文章没评论了」—— 真实情况是:那些评论对应的文章,本身已经不在了(已删除 / 仍是草稿 / 属于别的站), key 再怎么规范化也接不回来。线上数据是干净的。

不建议做的:

  • ❌ 批量把「伍比贰/演示站/往后」的评论导进来 —— 那是别的站的数据(含大量点赞记录)
  • ❌ 把 /yl、/27 等改成别的 key —— 没有正确的目标,改了反而污染
  • ❌ 恢复 494 条点赞记录 —— 线上设计本就不存点赞([LIKE] 记录当前为 0)

如果你更看重「把老评论展示出来」,唯一的正路是重建这些页面 (把已删文章从 git 历史恢复,或用 slug 重定向)。这个要单独评估,工作量比改 key 大得多。

但请注意:即使恢复了页面,能救回的也只是极小一部分—— 「优世界」站真实缺失的 101 条里,48 条属于 /yl(2022 年旧友链页), 41 条属于 /27 /13 /m/17 等旧站分页短链,只有 12 条是正经文章评论。

3.7 那「评论少了」的体感从哪来?

很可能来自这两点(都正常):

  1. 点赞不再算评论:原库 494 条点赞在导入时被过滤,前端看到的评论数自然变少。
  2. 老文章页面已删:文章没了,评论区自然也没了。

如果确实想提升「评论数」,更实际的做法是在新文章上做引导,而不是抢救旧数据。


四、certimate 能否迁到 Cloudflare

4.1 你现在的架构

certimate(跑在某台服务器上)
  ├─ 申请:litessl CA,*.usj.cc + usj.cc,DNS-01,tencentcloud-dns
  ├─ 部署:dogecloud-cdn(多吉云 CDN)
  └─ 部署:1panel website(那台服务器上的站点)
定时:10 12 * * *(每天 UTC 12:10)
失败邮件:177018615@qq.com

当前线上证书实测:

issuer  = LiteSSL RSA CA 2025(TrustAsia)
subject = CN=usj.cc
SAN     = usj.cc, *.usj.cc
有效期  = 2026-09-08 → 2026-12-07

4.2 certimate 支持 Cloudflare 吗?

支持,而且支持两种角色:

角色 支持 说明
DNS provider(申请用) ✅ 填 Cloudflare API Token 即可,用于 DNS-01 验证
Deploy provider(部署用) ✅ 可将证书部署到 Cloudflare

(certimate 官方:60+ DNS 托管商、120+ 部署目标,Cloudflare 在列。)

4.3 你的真实目标:「把服务器干掉」

需要先厘清一件事:usj.cc 现在到底谁在提供 HTTPS?

从实测响应头看:

Server: marco/3.2                    ← 又拍云
Via: T.206.M, V.403-zj-fud-202, ...  ← 又拍云多级节点

对外服务的是又拍云 CDN,不是那台服务器。那台服务器承担的是:

  • 跑 certimate(证书申请+分发)
  • 1Panel 上的 write-server / editor-api 等自建服务

所以「干掉服务器」要分清两件事:

① 只干掉「证书维护」这件事 → 完全可以,用 Cloudflare 托管 DNS + certimate 的 CF provider。

路径:

  1. 把 usj.cc 的 DNS 托管迁到 Cloudflare(当前在腾讯云 DNSPod)
  2. certimate 里把「DNS 提供商」从 tencentcloud-dns 换成 cloudflare(填 CF API Token)
  3. 证书申请照旧(litessl 或换 Let's Encrypt),DNS-01 走 CF API
  4. 部署目标改为「Cloudflare」(如果把站点也迁到 CF)或保留又拍云/多吉云

② 连「站点托管」也迁到 Cloudflare → 可行但有取舍:

优点 缺点
CF 免费版自带 Universal SSL(免申请免续期) 国内访问速度不如又拍云/多吉云(CF 免费版节点在境外)
不用再管 90 天续期 免费版 Universal SSL 只覆盖 example.com + *.example.com
有 CF Origin CA(15 年有效期)给回源用 想用 CF 自签源站证书需另配

4.4 ⚠️ 关键提醒:国内访问速度

你的读者主要在国内。又拍云/多吉云是国内 CDN,Cloudflare 免费版是境外节点—— 如果为了省一台服务器而把整个站点切到 CF,国内访问大概率变慢(首包延迟从几十 ms 变成几百 ms)。

所以我建议:

  • ✅ 可以做的:把 usj.cc 的 DNS 托管迁到 Cloudflare(CF DNS 免费、管理方便、API 完善), certimate 用它做 DNS-01,证书照旧签发后部署到又拍云 + 多吉云。这样证书维护不再依赖服务器上的 1Panel 部署环节。
  • ⚠️ 建议保留又拍云/多吉云做 CDN,不要为了「干掉服务器」把站点也搬到 CF。
  • ❌ 如果那台服务器还跑着 write-server / editor-api,那就不能关——关之前先确认没有别的服务在跑。

4.5 你现在到底能不能关服务器?

需要先回答这个问题:那台服务器上除了 certimate,还跑着什么?

  • 如果只有 certimate → 迁完就能关 ✅
  • 如果还有 write-server / editor-api(按记忆应该有)→ 关不掉 ⚠️

建议你在关之前先列一下那台机器上的服务清单。需要的话我可以帮你连上去盘一遍。


附:本次用到的排查命令

# CDN 缓存判别
curl -sI https://usj.cc/202610021614.html | grep -iE "server|age|last-modified|x-cache"

# D1 查询(curl 走 IPv4,Node fetch 会 ENETUNREACH)
curl -s -X POST \
  "https://api.cloudflare.com/client/v4/accounts/$ACC/d1/database/$DB/query" \
  -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
  -d '{"sql":"SELECT page_key, COUNT(*) FROM comments GROUP BY page_key"}'

# 线上是否有点赞记录
# → SELECT SUM(CASE WHEN content LIKE '%[LIKE]%' THEN 1 ELSE 0 END) FROM comments;  → 0