【学习笔记】AIHOT 拆解(六)技术栈:React 与 Next.js 的分野,与 React Router SSR / Fastify / pg-boss / PostgreSQL 17 / Compose 五容器 / EdgeOne CDN 逐件讲

12 min

整理日期:2026-09-29 调研方式:栈信息来自仓库 package.json(npm workspaces)、apps/ 三进程源码与 docs/deploy.md;黑盒时代的 Next.js/EdgeOne 推断引自本博客黑盒拆解。版本以 commit 589f79e 为准:react-router 8.4.0、fastify 5.12.5、pg-boss 12.34.0、postgres(驱动)3.4.9、PostgreSQL 17(容器)、Node >=24.11、TypeScript 7.0.2、Vite 8.3.1。 本系列:一·总览 · 二·信源与抓取 · 三·精选与评分 · 四·聚簇与热度 · 五·模型榜 · 六·技术栈(本篇) 写作动机:这套栈也常被读者问起——「为什么不用 Next.js」「队列为什么不用 Redis」。本篇把每个组件是什么、在 AIHOT 里干什么、换成别的会怎样,一次讲清。

一、太长不看

  1. React 和 Next.js 不是二选一:React 是 UI 库,Next.js 是建在 React 之上的全栈框架。选 Next.js = 选一套约定;不选它 = 自己攒(AIHOT 用 React Router 框架模式攒)。
  2. React Router v8 SSR 是 Next.js 的「自攒版」对手:v7 合并 Remix 团队的框架模式后,路由表、loader 数据加载、SSR 直出全都有,且构建走 Vite、部署只是个普通 Node 进程。
  3. Fastify 承担全部 HTTP 出口,靠 JSON Schema 校验和序列化快速路径换性能,Hook 体系挂安全策略。
  4. pg-boss 让队列白送:Postgres 的 SKIP LOCKED 就是队列,cron 调度内置,任务和业务数据同事务——不需要 Redis。
  5. PostgreSQL 17 不只是存储还是计算层:热度公式的去重和半衰期直接在 SQL 里算完。
  6. 五容器拓扑把安全规则做成物理隔离:web 不碰库、worker 独占模型调用,靠的是进程/容器边界而不是代码自觉。
  7. EdgeOne CDN 是黑盒时代的组件:线上站有边缘缓存层,开源快照刻意移除(换成你自己部署时的选项),但保留了 CDN 友好的钩子(图片代理的 auth_request 模式)。
  8. 整套选型的共性:组件少、每层可控、单机自部署友好——和「一个人带 AI 运营的生产系统」的约束自洽。

二、React 与 Next.js:上下层,不是同类

React 是一个 UI 库:只管「用组件和状态描述界面」——组件模型、Hooks、虚拟 DOM diff,输出渲染结果。它不关心路由、数据获取、构建、部署。纯 React 应用(如 Vite 脚手架)默认是浏览器里的 SPA:首屏空 HTML、JS 加载完才渲染、SEO 和首屏速度天然吃亏。

Next.js 是建在 React 之上的全栈框架,把 React 不管的事全管了:文件式路由、SSR/SSG、React Server Components、图片优化、API 路由、代码分割策略、部署适配(出身 Vercel,对其平台优化最深)。代价是约定多、心智模型重(App Router 的服务端/客户端组件边界、缓存语义几经大改)、深度绑定部署生态。

React(库)Next.js(框架)
层次渲染层全栈约定层
路由/数据/构建自己配(Vite + React Router 等)全套内建
SSR自己搭或用 React Router 框架模式一等公民
部署形态静态文件或任意 Node 服务器Node 服务器 / Vercel 最顺
适合纯前端 SPA、想掌控每一层内容站、要 SEO、全栈一体、图省事

所以「选 Next.js 还是 React」是个伪问题——真正的选择是「要框架的全套约定,还是自己攒」。AIHOT 黑盒时代是前者(Next.js App Router),重写后是后者(React Router 框架模式 + 独立 API 进程),差异见总览篇的黑白盒对照表。

三、React Router v8 SSR:薄全栈层

React Router 从 v7 起合并了 Remix 团队的框架模式:不再只是「React 的路由库」,而是完整的全栈框架——路由表约定(AIHOT 的 apps/web/app/routes.ts)、每路由的 loader 数据加载、SSR 开箱即用(服务端渲染成 HTML 直出、浏览器注水激活)、构建走 Vite(@react-router/dev)。

AIHOT 的用法很克制:server.ts 一个 Node 进程同时伺服构建产物和 SSR;web 层只通过 HTTP 读 api、不碰数据库;浏览器端页面缓存上限 300 秒(撤稿能及时到达读者的时效上限)。SSR 渲染路径上没有任何慢依赖,README 宣称的页面中位数 10ms 有架构支撑。对比 Next.js 路线:自部署 Docker 场景下更透明、进程边界清晰、不用迁就框架的部署假设——代价是这些好处都得自己动手保住。

四、Fastify:全部 HTTP 出口的守门进程

Node 生态的 Web 框架(与 Express 同类),设计更新:基于 JSON Schema 的请求校验和序列化快速路径(响应结构已知时跳过通用序列化,这是它比 Express 快的主因之一)、插件封装模型(作用域隔离,不污染全局)、生命周期 Hook、原生 async。

AIHOT 的 apps/api 用它承担所有 HTTP 出口:网站自用接口、公开 API v1、四路 RSS、MCP、后台接口、图片代理(HMAC 签名 URL)、OG 分享图渲染(satori + resvg)。仓库里那些「访问日志永不记 query string、鉴权头脱敏、OAuth 探测路径罐头 404、10MB 请求体上限、maxParamLength 300」都挂在它的 Hook 体系上——框架选型决定了安全策略挂载的优雅程度。

五、pg-boss:队列白送,Redis 不用了

pg-boss 是以 PostgreSQL 为存储的分布式任务队列:用 SELECT ... FOR UPDATE SKIP LOCKED 实现多 worker 安全消费,自带重试、延迟任务、cron 定时调度(AIHOT 的 24 个定时任务全靠它,时区 Asia/Shanghai)、单例锁(同任务不并发)、优先级。

AIHOT 的用法见总览篇第三节的定时任务表。选它的账很好算:系统本来就必须有 Postgres,队列白送,少一个要运维的基础设施;任务和业务数据同库——「分析结果 + 回执完成」能在一个事务里提交(analyzeArticle 正是这么做的)。代价是极限吞吐不如 Redis 系队列,但这个场景一天几万条任务,远没到瓶颈。

六、PostgreSQL 17:存储层兼计算层

关系型数据库,17 是 2024 年的大版本(增量备份、升级体验改进)。AIHOT 对它的用法有两个值得注意的倾向:

  • JSONB 重度使用:信源 config/cursor、付费调用的原始响应、榜单证据、判定候选……灵活 schema 的东西全进 JSONB(配合 GIN 索引和 ->> 查询);
  • 计算下沉 SQL:热度公式的参与者去重 + 半衰期衰减就是一条 SQL(GROUP BY participant_key + power(0.5, …));预算检查用咨询锁防并发超支;聚簇向量存 real[] 数组。每 5 分钟一次的热度重算在库内完成,比把信号拉回应用层算快且省内存。

60 张表覆盖内容管线、事件、报告、榜单、运营五个域(清单见总览篇)。

七、Docker Compose 五容器:拓扑即架构

docker compose up -d 起五个容器,职责边界就是架构图:

┌──────────────┐   ┌──────────────┐   ┌──────────────┐
│     web      │──▶│     api      │──▶│      db      │ PostgreSQL 17
│ React Router │   │   Fastify    │   └──────────────┘       ▲
│ SSR  :3000   │   │   :3001      │─────────────────────┐    │
└──────────────┘   └──────────────┘                     │    │
                        ▲                               ┌────┴─────┐
                        └───────────────────────────────│  worker  │
                                                        │ pg-boss  │
                                                        │ 队列+cron │
                                                        └──────────┘
   setup(一次性容器:跑数据库迁移+种子数据后退出)
  • db:Postgres 17,数据在它的卷里;
  • setup:init 容器模式——每次 up 先跑迁移和种子再退出,保证 schema 就绪;
  • api:唯一碰数据库的 HTTP 层;
  • worker:唯一调模型和付费 API 的层——「读者打开页面不触发模型调用」「密钥只在后端」这些铁律靠进程隔离兜底,而不是代码约定;
  • web:只读 api 的 SSR 层,单端口对外(server.ts 把 API 路径反代给 api 进程)。

加 --profile https 起第六个 Caddy 容器(自动 HTTPS,见总览篇附一)。数据在三个卷里:db、data(图片/缓存/本地备份)、caddy(证书);down 不删卷,down -v 才删。

八、EdgeOne CDN:黑盒时代的那一层

EdgeOne 是腾讯云的边缘平台(CDN + 安全防护 + 边缘函数一体),大陆节点友好、需 ICP 备案。它是黑盒拆解时从响应头指纹推断出的线上组件——当时 AIHOT 前面是 nginx/Ubuntu + 腾讯 EdgeOne。

开源快照里这一层被刻意移除:仓库默认裸跑 3000 端口,CDN 变成「你自己部署时的事」。但代码留了 CDN 友好的钩子——图片代理支持 nginx auth_request HEAD 子请求模式,就是为了让 HMAC 签名 URL 能被 CDN 安全缓存(签权在源站、缓存在边缘,两不误)。CDN 在这个架构里的价值:缓存静态资源和可缓存的页面响应(300 秒浏览器缓存上限就是撤稿时效与边缘命中率的平衡点)。

九、整体评价:这套栈的可抄性

每一层都有成熟替代(Next.js / Express / BullMQ+Redis / MySQL……),但这套组合的共性值得记住:组件最少化(一个数据库兼了队列和缓存的一部分)、每层一个普通进程(没有 serverless、没有运维玄学)、边界即安全(该隔离的用容器隔离)、对个人自部署最友好。当你也是一个(或一小队)人运营生产系统时,这套「少即是多」的选型思路比任何单项技术都值得抄。

附:与总览篇的差异说明

总览篇第三节给了三进程架构与四条铁律的骨架;本篇展开每个组件本身是什么、为什么是它、换成别的会怎样,并补齐黑盒时代栈(Next.js + EdgeOne)与开源栈的对应关系。版本号两篇一致。