把商品页做成 SSG 预渲染后,大促改价用户看到的还是旧价:渲染模式影响后端的 3 笔账
title: 把商品页做成 SSG 预渲染后大促改价用户看到的还是旧价渲染模式影响后端的 3 笔账topic: 前端渲染模式 SSR/CSR/SSG 对后端的影响batch: 6round: 3我们商品详情页原本是 CSR前端拉接口拼页面首屏慢、SEO 差被运营吐槽。前端同学一拍板改成 SSG——构建时把页面预渲染成静态 HTML理论上首屏飞快、SEO 也好。结果大促那天运营在后台把价格从 99 改成 79用户刷出来的还是 99半小时内几十个「价格不符」客诉还因为旧价更低引发一波超卖。这事锅不在前端在我们后端没接住「SSG 把动态数据写死」的连锁反应。渲染模式从来不是前端自己的事它直接决定后端接口被怎么调用、被调用多少次、数据新鲜度由谁负责。这篇文章用 3 个真实账讲清 SSR/CSR/SSG 到底怎么影响你写后端。事故一SSG 把价格和库存写进静态页改价不生效前端 SSG 构建时的抓取逻辑简化// 构建期执行把商品数据预渲染进 HTML export async function getStaticProps({ params }) { const product await fetch(https://api.internal/product/${params.id}) .then(r r.json()); return { props: { product } }; // 数据在构建时定格 }逐行解释为什么这会坑到后端- 第 3 行fetch(...)在构建时跑一次拿到的价格/库存被写死进生成的静态 HTML。构建之后这次数据再也不会更新除非重新构建。- 第 5 行props: { product }把当时那一份数据固化成页面。大促运营改价改的是数据库但静态页里还是构建时的 99 元——用户看到的永远是旧值直到下次构建。- 后端这边我们原本以为「价格以接口实时返回为准」SSG 之后这个假设被打破数据新鲜度从「接口实时」变成「构建时快照」而构建频率我们一天构建 2 次远低于运营改价频率大促几分钟一次。后端和前端的「数据时效契约」在 SSG 下彻底错位。后端该怎么做把「动态部分」从 SSG 里摘出来正确姿势是「静态骨架走 SSG动态数据走 CSR/边缘实时」// 后端给前端的商品接口拆成两份语义 RestController RequestMapping(/product) public class ProductController { GetMapping(/{id}/static) // 适合 SSG 构建期抓的标题、描述、规格 public ProductStatic staticPart(PathVariable long id) { ... } GetMapping(/{id}/realtime) // 价格、库存、活动——必须实时禁止进静态页 public ProductRealtime realtime(PathVariable long id) { return realtimeCache.get(id); // 带短 TTL 的缓存防击穿 } }逐行解释- 第 6 行/static只返回「构建期不会变或变得不频繁」的字段标题、图文、规格这些进 SSG 静态页没问题。- 第 10 行/realtime返回价格、库存、活动标签——这些必须在客户端渲染时实时拉绝不许进静态页。前端 SSG 只预渲染骨架价格和库存用一小段 CSR/ hydration 从/realtime补。- 第 11 行realtimeCache.get(id)提醒实时接口被首屏 CSR 调用QPS 会很高等于页面访问量必须加短 TTL 缓存比如 1 秒挡击穿否则 SSG 没救成反而把价格接口打挂。我们当时没加这层价格接口在改价那刻被刷爆。- 这条分界线静态 vs 实时必须由后端在接口设计阶段就划清并和前端约定「哪些字段允许进静态页」——这是 SSG 能用的前提否则就像我们那样把价格写死。事故二SSR 把用户态接口放服务端渲染放大 N 倍打挂推荐服务另一个事故在「个人中心」页。前端从 CSR 改成 SSR服务端渲染想让首屏快于是把「根据用户 ID 拉个性化推荐」的逻辑也放到了服务端渲染阶段// 服务端渲染时调用Node 侧 async function getServerSideProps(ctx) { const uid ctx.req.cookies.uid; const rec await fetch(https://rec.internal/user/${uid}/feed); // 服务端代发 return { props: { rec: await rec.json() } }; }逐行解释为什么这会把后端打挂- 第 4 行fetch(.../user/${uid}/feed)在每个请求的服务端渲染时由 Node 代发。CSR 时代这个调用是「浏览器直连推荐服务」连接分散在用户侧SSR 后变成「所有用户请求先打到 NodeNode 再集中转发给推荐服务」——推荐服务的入向连接从「浏览器分散」聚成「Node 集群集中」QPS 被放大数倍。- 更糟SSR 是「请求来了才渲染」没有 CSR 那种「客户端缓存/防抖」每次页面请求都实打实打一次推荐服务。我们上线 SSR 后推荐服务 QPS 涨了约 5 倍直接把它的线程池打满连带影响真正该用的场景信息流。- 根因前端为了首屏把「用户态、高成本」的接口挪到了服务端却没评估这些接口的「被调用放大效应」。SSR 适合「公共、可缓存」的内容商品详情、文章不适合「每人不同、高成本」的个性化接口。后端应对SSR 的用户态接口必须带缓存 降级RestController RequestMapping(/user) public class FeedController { GetMapping(/{uid}/feed) public Feed feed(PathVariable long uid) { // SSR 场景下这个接口会被集中调用必须短 TTL 缓存 降级 Feed f feedCache.get(uid, () - { try { return recommendService.recommend(uid); } catch (Exception e) { return Feed.EMPTY; } // 推荐挂了返回空别拖垮 SSR }); return f; } }逐行解释- 第 6 行feedCache.get(uid, ...)给每个用户短 TTL我们 3 秒缓存SSR 集中调用时大部分命中缓存推荐服务实际 QPS 被削平到「每用户每 3 秒一次」比原来放大 5 倍时降了几十倍。- 第 8 行catch降级返回Feed.EMPTYSSR 渲染个性化推荐属于「增强项不是必需项」推荐挂了就空着绝不让推荐服务的故障拖垮整个页面的 SSR否则用户连页面都打不开。这是「非核心依赖必须降级」的原则SSR 把后端接口收拢后这个原则更关键。- 结论适合 SSR 的是「公共 可缓存」的内容用户态高成本接口进 SSR必须缓存 降级否则就是给后端埋放大器。三种模式对后端的影响一张表模式谁调后端接口后端被调用量数据新鲜度后端要注意CSR浏览器直连分散天然防抖实时接口要扛住浏览器并发做好限流SSRNode 服务端代发集中放大易打挂请求时实时用户态接口必须缓存降级公共内容才适合SSG构建期抓一次极低仅构建构建时快照动态字段禁止进静态页拆 static/realtime我的取舍选模式别只看前端首屏。看「数据新鲜度要求」和「是否用户态」公共、变化慢、要 SEO → SSG但动态字段隔离公共、要实时、首屏要快 → SSR只渲染公共部分用户态、个性化 → 留在 CSR别塞进 SSR 放大后端。后端工程师必须参与「哪些字段进静态/服务端渲染」的决策否则就像我们前端一改渲染模式后端接口被放大打挂或数据写死锅都甩不到前端头上。第三个账CSR 的 SEO 问题反过来逼后端加「预渲染接口」CSR 的坑是 SEO 差——爬虫不执行 JS抓不到内容。我们为了 SEO 给商品页做 SSR/SSG前端要后端额外提供「适合预渲染的纯数据接口」不带用户态、不带个性化。这其实是笔正向账它逼我们把「页面数据」和「用户数据」在接口层彻底分开长期看架构更清晰。但代价是后端多维护一套「静态数据接口」的 SLA——它一旦挂所有静态页都渲染不出来SSG 有构建缓存兜底SSR 没兜底直接 500。所以 SSG 比 SSR 在「后端依赖」上更稳SSG 只在构建时依赖一次SSR 每次请求都依赖。这也是我们最终商品详情选 SSG动态字段隔离、个人中心留 CSR 的原因。SSR 的渲染超时后端要留「降级到 CSR」的开关SSR 把渲染放到了请求链路里就意味着「渲染失败 页面 500」。我们有次推荐服务抖动SSR 调它超时整个商品页直接 500比 CSR 时代推荐挂了页面照样开、只是少块内容惨得多。// 后端给 SSR 的接口必须带超时 降级标记让前端能「SSR 失败就退 CSR」 GetMapping(/{id}/realtime) public ResponseEntityProductRealtime realtime(PathVariable long id) { ProductRealtime r realtimeCache.get(id, () - recommendSafe(id)); return ResponseEntity.ok() .header(X-Render-Fallback, csr) // 告诉前端这接口可降级SSR 拿不到就 CSR 补 .body(r); }逐行解释- 第 5 行recommendSafe(id)包了降级推荐挂了返回空而不是抛异常SSR 至少能渲染出「无推荐」的页面不会整页 500。- 第 7 行X-Render-Fallback: csr是和前端约定的降级信号这个接口的数据如果 SSR 阶段取不到前端就走 CSR 在客户端补而不是卡在 SSR 等超时。降级开关必须由后端在接口契约里给出不能前端自己猜。- 我的经验凡是要进 SSR 的接口默认都得「可超时、可降级、可缓存」否则一次后端抖动就变成一次整页故障。CSR 时代这些接口挂了只是少块内容SSR 时代挂了是整页打不开——故障爆炸半径被渲染模式放大了后端必须 correspondingly 把可靠性做厚。复盘真实数字SSG 写死价格大促 30 分钟内 47 起价格客诉 约 120 单超卖旧价更低紧急回滚到 CSR 建/realtime接口后收敛。SSR 放大推荐服务上线后推荐服务 QPS 涨约 5 倍从 8k 到 41k线程池打满加 3 秒缓存 降级后回到 9k稳定。拆 static/realtime 接口后商品页首屏仍快SSG 骨架价格实时CSR 补后端价格接口加 1 秒 TTL 缓存峰值 QPS 削掉 60%。我的取舍渲染模式是后端接口设计的输入不是前端的私事我不建议把「选 CSR/SSR/SSG」完全丢给前端。它直接决定你的接口被谁调浏览器还是 Node、被调多少次分散还是集中放大、数据新鲜度谁负责实时还是构建快照。公共且变化慢的内容用 SSG 最省后端公共且要实时的用 SSR 但只渲染公共部分用户态个性化的留在 CSR。无论选哪个后端都要划清「static / realtime」字段边界给 SSR 的高成本接口加缓存和降级。渲染模式选错后端不是「慢一点」是「被放大打挂」或「数据写死引发客诉」——这两类都比首屏慢几秒贵得多。思考题如果你的商品详情页要同时兼顾「SEO要静态可抓」和「价格库存实时要动态」你会怎么在前端和后端之间切这道边界SSG 骨架 客户端补实时数据和 SSR 全量服务端渲染哪个对后端的「故障爆炸半径」更小

相关新闻

计算机专业大学生如何做好职业规划?

计算机专业大学生如何做好职业规划?

大家好,我是袁庭新。西安工程大学2025级人工智能专业的一位同学今年大一,面对未来的大学学习和生活有些迷茫,问了我几个问题,我想这也是在校大学生的共性问题,所以把我与这位同学的交流互动内容也分享给大家。学生问&a…

2026/10/15 3:15:32 阅读更多 →
一个 @JsonIgnore 漏写,把用户密码序列化到了前端:序列化选型的 4 个真实坑

一个 @JsonIgnore 漏写,把用户密码序列化到了前端:序列化选型的 4 个真实坑

title: 一个 JsonIgnore 漏写,把用户密码序列化到了前端:序列化选型的 4 个真实坑 topic: 序列化框架对比:Protobuf、JSON、Hessian batch: 6 round: 3我们用户服务把 User 对象直接 JSON.toJSONString 丢给前端,DTO 里图省事复用…

2026/10/7 22:10:06 阅读更多 →
一条行为序列正则把风控接口线程池打满:ReDoS 的 3 个隐藏触发点

一条行为序列正则把风控接口线程池打满:ReDoS 的 3 个隐藏触发点

title: 一条行为序列正则把风控接口线程池打满:ReDoS 的 3 个隐藏触发点 topic: 正则表达式引擎原理与性能陷阱 batch: 6 round: 3我们风控系统要判断「用户是否完成了 点击→浏览→加购→购买 的转化路径」,早期同学写了个正则直接匹配上报的行为序列字…

2026/10/3 23:57:44 阅读更多 →

最新新闻

多个传感器如何协同作战?World2Agent多传感器组合与fanout传输完整指南

多个传感器如何协同作战?World2Agent多传感器组合与fanout传输完整指南

【免费下载链接】world2agent World2Agent(W2A) is an open protocol that standardizes how Al agents perceive the real world. 项目地址: https://gitcode.com/gh_mirrors/wo/world2agent 点击查看 免费下载 World2Agent(W2A)是一个开放…

2026/10/15 19:45:41 阅读更多 →
PASCAL-5i 小样本语义分割数据集复现要点与踩坑指南

PASCAL-5i 小样本语义分割数据集复现要点与踩坑指南

简介:Pascal-5i数据集是面向小样本学习研究的标准化数据集,源自PASCAL VOC,将20个类别划分为基础类与新类,支持5-way 1-shot和5-way 5-shot任务,适合需要验证原型网络、元学习或知识迁移方法的科研人员与算法工程师使用…

2026/10/15 19:45:41 阅读更多 →
c9-python-getting-started 系列:用 asyncio 与 async/await 编写 Python 异步操作

c9-python-getting-started 系列:用 asyncio 与 async/await 编写 Python 异步操作

示例工程教程 【免费下载链接】c9-python-getting-started Sample code for Channel 9 Python for Beginners course 项目地址: https://gitcode.com/gh_mirrors/c9/c9-python-getting-started 点击查看 免费下载 本指南围绕 more-python-for-beginners 课程中「As…

2026/10/15 19:45:41 阅读更多 →
高光谱数据加载实战:从HDF5到PyTorch DataLoader的完整路径

高光谱数据加载实战:从HDF5到PyTorch DataLoader的完整路径

简介:面向使用PyTorch框架处理高光谱图像的研究者,这份资源精准解决高光谱数据在深度学习训练中批量读取与预处理的效率问题。高光谱图像通常包含数百个光谱通道,数据量庞大,因此加载策略直接影响模型训练速度与稳定性。压缩包共七…

2026/10/15 19:45:41 阅读更多 →
昇腾910B/910C 多模态模型私有化部署实战:11 个模型 + Nginx 统一 API 网关

昇腾910B/910C 多模态模型私有化部署实战:11 个模型 + Nginx 统一 API 网关

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/15 19:45:41 阅读更多 →
协处理器系统集成实战:架构设计、接口实现与性能调优

协处理器系统集成实战:架构设计、接口实现与性能调优

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/15 19:44:39 阅读更多 →

日新闻

Docker Compose 容器化部署 ChirpStack LoRaWAN 网络服务器实战

Docker Compose 容器化部署 ChirpStack LoRaWAN 网络服务器实战

1. 为什么选择容器化方式部署 LoRa 网络服务LoRa 物联网项目落地时,网络服务器是整个链路的核心枢纽。它负责接收网关上报的数据包、完成去重与解密、再把有效载荷分发给上层应用。早期大家习惯把网络服务器直接装在物理机上,依赖一堆系统库和特定版本的…

2026/10/15 0:00:46 阅读更多 →
共享LLM服务下的跨模型自动扩缩容:Kubernetes实战与避坑指南

共享LLM服务下的跨模型自动扩缩容:Kubernetes实战与避坑指南

1. 共享推理服务里,扩缩容到底难在哪做过大模型推理服务部署的人,大概都经历过这样的场景:一个团队共用一套推理集群,A 业务跑的是 7B 的小模型,B 业务跑的是 72B 的大模型,C 业务白天流量高、晚上几乎归零…

2026/10/15 0:00:46 阅读更多 →
Systemd 启动阶段根文件系统只读之谜:fstab 与 remount-fs 服务深度解析

Systemd 启动阶段根文件系统只读之谜:fstab 与 remount-fs 服务深度解析

1. 引子:一次内核恐慌引发的追查先从一个真实场景说起。某次我给一台老旧的嵌入式设备折腾系统裁减,为了把启动时间压进两秒以内,我手贱地把一串自认为“没用”的 systemd 服务给 disable 了,其中就包括systemd-remount-fs.servic…

2026/10/15 0:00:46 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/15 16:18:54 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/15 19:41:51 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/15 13:48:15 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/15 0:14:36 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/14 22:45:47 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/15 9:49:14 阅读更多 →