叮叮当当 #3:热榜聚合站季度运行复盘与收官

一、这一篇讲什么:收官

热榜聚合站系列到这里收口。第一篇讲选型:dailyhot-api 主力、RSSHub 补位、redis 中转、Next.js 聚合渲染、SearXNG 做可选搜索件,五容器跑在阿里云 2C1.8G 单机上;第二篇讲搭建与踩坑:compose 编排、nginx 反代、五个真实坑位。这一篇是季度运行复盘——跑了一个季度之后,哪些组件稳、哪些折腾、出过什么事、花了多少钱、重来一遍会怎么改、以及这个站接下来怎么办。

先把口径交代死,免得后面所有内容被误读:这个站是自托管的自用信息面板,从头到尾没装任何统计、没有公网流量数据。所以本文没有 PV/UV、没有”增长”、没有任何运营叙事——不是谦虚,是没这个数据,硬编一个就是吹牛。复盘的三个坐标系是:作为工具好不好用、作为一套栈稳不稳、作为一笔开销贵不贵。性能与资源占用照旧全定性,一个没测过的数字都不给。

二、运行画像:哪些组件稳,哪些折腾

一个季度下来,五个容器的”性格”分化得非常清楚:

组件 定性表现 折腾度
dailyhot-api 五个里最省心的:常驻内存低且平稳,从没因自身原因重启过 几乎为零
redis 稳定,但经历过一次策略调整(见第三节),是”需要动脑”的稳定 低,一次性
app(Next.js) 稳定;内存常驻在限额预算内,无泄漏迹象(docker stats 巡检口径) 低
RSSHub 功能正常,但路由可用性跟着上游平台改版走,一个季度内需要照看数次(见第三节) 中,持续性的
SearXNG 功能正常,但偶发 CPU 上浮,是全栈资源观感最差的一块(见第三节) 中偏高

这张表背后有个清晰的分层:数据链路上的组件(抓取、缓存、展示)全都稳,麻烦全出在”附加价值”组件上。RSSHub 的折腾是外部性的——它没坏过,是它盯着的那些平台在变;SearXNG 的折腾是结构性的——搜索天然是并发行为,小机器天然不喜欢并发。资源占用的整体画像:全栈常驻内存没有突破过各容器限额(巡检口径的定性结论),机器整体没有出现过因这套栈导致的内存压力告警——”人人有限额”这条第一篇立下的纪律,一个季度下来没有还债。

巡检的惯例也值得留档,它便宜到没有理由不做:docker stats --no-stream 扫一眼各容器常驻内存与 CPU、页面上抽查两张卡片的更新时间戳、redis-cli INFO stats 看驱逐计数是否还在涨。三件事一分钟,每周一次——本季度的三起事件里有两起(SearXNG 尖峰、redis 驱逐频繁)就是这套巡检先发现的,而不是页面先坏的。自用系统没有告警系统,巡检就是告警系统。

三、故障与处理记录:三件事

事件一:RSSHub 路由失效(外部性故障)

现象:某平台热榜卡片突然只剩旧数据,时间戳停更。定位:dailyhot 同平台的卡片是新鲜的,说明不是网络或 redis 的问题;进 RSSHub 容器直接请求对应路由,返回空——上游平台改了页面结构,该路由的解析失效,等社区修复。处理分三层:一是该源的卡片靠 redis 里旧数据挂着,标”更新于”时间,页面不黑;二是 dailyhot 有同平台源的,临时把卡片切到 dailyhot 数据;三是社区新版本发布后 docker compose pull rsshub 升级,路由恢复。教训:这是选型时就该预期到的成本——用社区路由库,就是把自己的可用性外包给社区响应速度;第一篇选 dailyhot 当主力、RSSHub 只补位的架构,在这次事件里正好体现了价值:主力源稳定,补位源坏了不致命。反模式一:路由一失效就自己动手改 RSSHub 的路由源码顶上。救一次火没问题,救三次就是接盘——把”社区维护、我白用”的成本模型,悄悄改写成”上游每改一次版我加班一次”,业余维护者扛不住这个节奏,最终下场是整个源烂掉。正确姿势:切兜底数据源、等社区、升级,三步之外的事不做。

事件二:SearXNG 偶发 CPU 上浮(结构性故障)

现象:巡检时偶尔看到 SearXNG 容器 CPU 冲高一阵又自己落回去,频率不定,内存倒是安分。定位:这类”自己会好”的尖峰不值得上火焰图全量分析(真需要的时候,站内《CPU 100% 的定位路径》是完整方法),直接从行为推断:搜索是并发打多个上游引擎,一次稍复杂的查询就足以在小机器上掀起一阵 CPU;多个查询叠加就更高。处理:确认它被限额和 restart 兜着(最坏是被杀重启,不连坐全栈),然后把巡检里对它的预期从”不该尖峰”改成”尖峰正常、常驻异常才报警”。结论落在架构上:它仍然是全栈里第一个该被砍的可选件——这句话第一篇说过,一个季度的运行画像没有推翻它,反而坐实了它。这件事的通用教训值得单独点出来:对自愈型异常,先判断”边界是否兜得住”,再决定要不要深挖——限额兜得住就调预期、留观察,兜不住才值得投入完整定位;把排查预算花在会连坐的故障上,是单机自托管项目的资源观。

事件三:redis 内存策略调整(配置性故障)

现象:某段时间榜单卡片”变旧”的频率明显上升,检查发现 redis 驱逐频繁——缓存里的榜单数据过早被逐出,下次抓取窗口到来前页面只能挂更旧的数据。定位:maxmemory 初设得保守,而键的数量随源和分页缓存增长,实际工作集慢慢顶到了上限;allkeys-lru 在工作集大于内存时会把”还有用的”数据也逐出去。处理两步:一是按”源数量 × 每键大小 × TTL 窗口”重新估了工作集,把 maxmemory 上调一档(仍在容器限额内、仍给 fork 留余量);二是给各源 TTL 加了抖动、错开过期时刻,避免工作集同涨同落。纪律没变的是 policy 仍是 allkeys-lru——因为这台 redis 依旧只装可重建的缓存数据,业务数据混进来的前提不存在。驱逐策略的完整选型逻辑与八种 policy 的坑,站内《Redis 内存打满与驱逐策略》是专门展开,这次调整本质上就是那篇结论的一次实操。反模式二:给会增长的缓存一次性设死内存参数、之后不复查。缓存的工作集不是常量——源在加、分页在存、TTL 窗口在变,初值再准也只对得起设它的那一天。缓存类参数要进巡检清单(看驱逐计数、看命中率),让数据替你说”该调了”,而不是等卡片变旧倒逼排查。

四、成本账:一台服务器的复用经济学

这套站的成本结构简单到一张表说完:

成本项 实际情况 说明
服务器 0 新增 复用跑博客主站的那台 2C1.8G,热榜站以内存预算的形式”租”其中一块
域名与证书 0 新增 子域挂在主域下,证书与续期复用主站那套
带宽/流量 忽略级(定性) 自用面板,访问量是个位数里的个位数
CI 与镜像存储 忽略级(定性) 只有 app 一个自建镜像,公开 registry 免费额度内
人力 不免费 一个季度约等于”三次照看 + 一轮策略调整”,全记在第三节

现金成本约等于零,但要把隐藏成本说透:省钱的代价是同机故障域。热榜栈与博客主站共享一台机器的内存与 CPU,任何一边失控理论上都会挤到另一边——这正是”全限额 + 只绑回环口 + 不本机构建”三条纪律存在的理由,它们不是优化项,是同机复用的安全边界。换一台独立小机器的月成本不高,但会引入新的运维面(多一台机器的证书、监控、更新);在自用定位下,这个交换不划算。

五、如果重来一遍会怎么改

复盘的价值在这张表。左边是当时的选择,中间是重来会做的,右边是理由:

当时的决策 重来会怎么做 为什么
SearXNG 与全栈同机 要么一开始就不装,要么放独立最便宜的小机器 它是全栈唯一”结构性费资源”的组件,且与核心数据链路零耦合;同机部署让全栈为一个可选件承担内存与 CPU 预算
redis maxmemory 拍脑袋定初值 开跑前先按”键数量 × 单键大小 × TTL 窗口”估算工作集再设 事件三本来可以不发生;驱逐频繁才调,等于用线上症状补作业
抓取 cron 跑在 app 容器里 抓取独立成容器/脚本,与展示解耦 app 重启或升级时抓取跟着断;解耦后两边互不拖累,将来要外移抓取(第一篇的对外预案)也是现成接口
第三方镜像用浮动 tag 钉 digest,升级显式拉新 事件一里”pull 一下就好”的从容,另一面就是”任何一次 pull 都可能带入未知变化”;钉 digest 让每次升级都是明确的决定
Next.js 做展示层 维持不变 聚合页需要服务端读 redis 动态渲染,纯静态方案不成立;且复用已有知识栈的收益一个季度都在兑现
dailyhot 主力 + RSSHub 补位 维持不变 事件一的实测反过来验证了这个分工:主力稳、补位可牺牲

四条要改、两条维持——对一次”约束优先”的选型来说,这个成绩单是体面的,但要改的四条恰好都指向同一个错误习惯:把”先跑起来”的临时决定留成了长期配置。SearXNG 是”顺手也装上”,maxmemory 是”先给小点”,cron 是”塞 app 里省事”,浮动 tag 是”方便升级”——每一条单看都合理,合起来就是运维面的慢性债。

六、下一步:对外开放与 API 的决策

收官前把两个悬而未决的问题正面回答掉,都用决策链不用感觉。

对外开放吗?不。三个硬约束摆着:机器(2C1.8G 与主站同机,前面说过故障域问题);责任(对外就是运营一个内容聚合站,上游内容的使用授权、平台条款、被上游封禁的应对,全是自用场景不存在的负担);观测(现在连访问统计都没有,对外开放需要的监控、告警、限流一套都没建)。把”开放前必须补的课”列出来,差距有多远一目了然:

开放前必须补的课 现状 差距定性
容量与隔离 全栈与博客同机,全限额 需要独立机器或至少独立抓取层;SearXNG 必须先砍
监控与告警 每周人工巡检三件事 从”人肉周检”到”对外可用”,中间差一整套告警闭环
限流与防滥用 无(自用不需要) 公开页面与未来 API 都需要,否则一个爬虫就能把小机打满
内容合规 自用展示,无分发问题 对外分发涉及上游平台条款,需要逐源评估,这是最重的一课

这张表读出来的结论比”不”更具体:不是永远不,是这四门课一门都没修。真到要开放的那天,顺序也是先砍 SearXNG、再外移抓取、最后升配——这个顺序第一篇就写死了,一季度的运行画像没有给出任何修正它的理由。

开放 API 吗?不承诺。技术上最便宜:redis 里数据现成,app 加一个只读 JSON 路由就能输出。但公开 API 等于对陌生人承诺可用性,而这套栈的可用性上限是”上游平台 + 社区路由库 + 一台小机器”三者的交集——自己用它可以接受这个上限,把它写成对外承诺就是不诚实。折中做法:不开放公开端点,自己的脚本直接读 redis(本机回环口),需要数据的地方已经有数据。

收官总结

三篇走完,这套栈教会的东西收敛成几条:约束先于选型——内存、端口、单机三条约束直接杀掉了大部分候选,剩下的决策反而好做;默认值在小内存机器上全部可疑——监听地址、时区、限流、内存策略,每一个都值得怀疑一遍;限额是纪律不是优化——它把”失控”这个状态从”机器打穿”降级成”一个容器被杀”;可选件要按”砍掉它系统是否成立”来装——SearXNG 一个季度的表现反复确认这条;以及最诚实的一条:这套栈的”稳”,一半功劳是纪律,另一半是它只服务一个人的访问量——把这句话留在这里,将来谁(包括未来的我)想把它开放出去,先回来把这一段重读一遍。

系列的分工最后对齐一遍:第一篇是选型决策链(为什么是这五个组件),第二篇是搭建与坑位(怎么跑起来、默认值怎么撞墙),本篇是运行后的账本(稳不稳、贵不贵、改什么、走不走)。三篇合起来是一个完整的小项目生命周期记录,素材真实、数字克制——如果它对读者有价值,价值大概率不在”照抄这套栈”,而在”把约束、纪律、复盘这三件事套到自己的小项目上”。系列到此封箱:除非对外开放或 API 的决定真的翻案,不会再有第四篇。

版本核实(撰写时检索):涉及的组件 RSSHub(DIYgod/RSSHub)、DailyHotApi(imsyy)、SearXNG、redis、Next.js 均为活跃维护的开源项目,版本以各自官方仓库为准,本文不钉任何版本号。架构与三起事件处理记录为本站真实实践;资源占用、成本、频率全部为定性描述,本文无任何实测性能数字,也无任何公网流量数据。

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇