首页/新闻资讯/正文详情

边缘渲染的 CDN Worker 实践:在离用户最近的地方执行 SSR

发布时间:2026/9/26 11:33:57 来源:云帆数科 栏目:资讯中心
边缘渲染的 CDN Worker 实践:在离用户最近的地方执行 SSR
边缘渲染的 CDN Worker 实践在离用户最近的地方执行 SSR一、SSR 服务器在北京深圳用户打开页面要 1.2 秒——一半时间花在网络传输上边缘渲染不是新概念Cloudflare Workers 和 Vercel Edge Functions 已经推了好几年。但实际落地中把 SSR 从中心化服务器搬到 CDN 边缘节点面临三个问题运行时差异Node.js vs WinterCG/Web API、框架限制React 18 RSC 的边缘支持还不太成熟、安全风险用户的认证 token 在 CDN Worker 里处理。边缘渲染适合的页面类型首屏加载速度是核心 KPI如电商详情页、内容相对静态但有个性化组件如用户名和购物车角标、用户分布全球。不适合的大量数据库查询的页面边缘节点没有直连数据库的能力、强状态依赖的页面如实时协作编辑器。核心思路是边缘 SSR 动态部分的水合页面的静态部分在边缘渲染Html Shell、CSS、静态文案动态部分用户信息、个性化推荐用占位符渲染在客户端通过懒加载水合。二、底层机制与原理剖析边缘渲染的三个层次全页缓存如果页面完全不需要个性化在边缘 Worker 中缓存完整的渲染结果。缓存 key 基于 URL 设备类型Desktop/Mobile。缓存时间可以按页面特性设置首页 60 秒、文章页 1 小时。模板缓存 动态拼接页面的 HTML 框架在边缘缓存动态部分用户信息、购物车数量通过 API 调用获取后在 Worker 中用 HTMLRewriter 或字符串替换拼入。完全 SSR对于需要完整 SSR 的页面Worker 执行完整的渲染逻辑。但受限于 Worker 的运行时间限制Cloudflare Workers 最多 30 秒 CPU 时间和内存限制128MB需要精简渲染逻辑。三、生产级代码实现// edge-worker.js /** * CDN Worker 边缘 SSR 实现 * * 运行在 Cloudflare Workers / Vercel Edge 环境 * 设计要点 * 1. 不使用 Node.js APIfs, process, path只用 Web API * 2. 流式渲染——先返回头部 HTML动态内容后追加 * 3. 缓存分层——模板缓存长 数据缓存短 */ // --------------------------------------------------------------------------- // HTML 模板缓存存储在边缘 KV 中 // --------------------------------------------------------------------------- const HTML_TEMPLATES { home: !DOCTYPE html html langzh-CN head meta charsetUTF-8 title{{title}}/title link relstylesheet href/static/main.css !-- 预连接关键域名 -- link relpreconnect hrefhttps://api.example.com /head body header nav a href/首页/a div iduser-badge{{userSkeleton}}/div /nav /header main{{content}}/main script defer src/static/hydrate.js/script /body /html, }; // --------------------------------------------------------------------------- // 请求处理器 // --------------------------------------------------------------------------- async function handleRequest(request, env, ctx) { const url new URL(request.url); const path url.pathname; // 静态资源——直接走 CDN 缓存 if (path.startsWith(/static/) || path.startsWith(/_next/static/)) { return fetch(request); // 让 CDN 原生缓存处理 } // API 请求——代理到源站 if (path.startsWith(/api/)) { return proxyToOrigin(request); } // 页面请求——边缘 SSR return handlePage(request, env, ctx, path); } /** * 页面 SSR 处理 */ async function handlePage(request, env, ctx, path) { // 1. 尝试从 KV 读取缓存的完整 HTML const cacheKey page:${path}:${getDeviceType(request)}; const cached await env.PAGE_CACHE.get(cacheKey); if (cached !shouldBypassCache(request)) { // 带上缓存的 age header return new Response(cached, { headers: { Content-Type: text/html; charsetutf-8, X-Cache: HIT, }, }); } // 2. 并行获取所需数据 const [userData, pageData] await Promise.all([ fetchUserData(request, env), fetchPageData(path, env), ]); // 3. 渲染 HTML const html renderTemplate(home, { title: pageData.title || 首页, userSkeleton: renderUserSection(userData), content: renderContent(pageData), }); // 4. 缓存渲染结果 // 非登录用户缓存 60 秒登录用户不缓存个性化内容 if (!userData.isLoggedIn) { ctx.waitUntil( env.PAGE_CACHE.put(cacheKey, html, { expirationTtl: 60 }) ); } return new Response(html, { headers: { Content-Type: text/html; charsetutf-8, X-Cache: MISS, // 浏览器缓存策略 Cache-Control: userData.isLoggedIn ? private, no-cache : public, s-maxage60, stale-while-revalidate300, }, }); } // --------------------------------------------------------------------------- // 数据获取边缘环境——Web API only // --------------------------------------------------------------------------- async function fetchUserData(request, env) { const cookie request.headers.get(Cookie) || ; const sessionToken extractCookie(cookie, session); if (!sessionToken) { return { isLoggedIn: false }; } try { const resp await fetch(${env.API_ORIGIN}/api/user/me, { headers: { Authorization: Bearer ${sessionToken}, // 传递客户端 IP用于风控 X-Forwarded-For: request.headers.get(CF-Connecting-IP) || , }, // 设置短超时——用户数据 API 必须快 signal: AbortSignal.timeout(2000), }); if (resp.ok) { const user await resp.json(); return { isLoggedIn: true, ...user }; } } catch (e) { // 用户 API 不可用——降级为匿名用户 console.error(User API failed:, e.message); } return { isLoggedIn: false }; } async function fetchPageData(path, env) { try { const resp await fetch(${env.API_ORIGIN}/api/page${path}, { signal: AbortSignal.timeout(3000), }); return resp.ok ? await resp.json() : { title: 页面加载中... }; } catch { return { title: 页面暂时不可用, error: true }; } } // --------------------------------------------------------------------------- // 模板渲染简单的字符串替换 // --------------------------------------------------------------------------- function renderTemplate(templateName, data) { let template HTML_TEMPLATES[templateName]; if (!template) return ; for (const [key, value] of Object.entries(data)) { template template.replace(new RegExp(\\{\\{${key}\\}\\}, g), value || ); } return template; } function renderUserSection(userData) { if (!userData.isLoggedIn) { return a href/login classlogin-btn登录/a; } return div classuser-info>

相关推荐

Agent 故障隔离与自愈:单个 Agent 异常不影响其他会话
Agent 故障隔离与自愈:单个 Agent 异常不影响其他会话

Agent 故障隔离与自愈:单个 Agent 异常不影响其他会话一个 Agent 崩了不可怕,可怕的是一个崩了把二十个正常的也拉下水。一、场景痛点 你们的 Agent 平台上线了 500 个并发会话,每个会话对应一个独立的 Agent 实例。一切运行良好,… · 2026/9/23 23:19:47

Windows 10下Claude HUD显示异常的排查与修复
Windows 10下Claude HUD显示异常的排查与修复

1. 问题现象与初步排查最近在Windows 10系统上安装Claude HUD时遇到了显示异常问题。HUD界面完全无法显示,但任务管理器显示进程正常运行。这种情况在游戏开发者和模拟器用户中并不少见,通常与图形渲染或窗口管理相关。首先检查了系统日志(通… · 2026/9/12 22:10:55

C++与OpenCV实战:基于几何特征的工业视觉模式识别
C++与OpenCV实战:基于几何特征的工业视觉模式识别

1. 项目概述:从“识别形状”到“理解结构”“基于几何特征的模式识别”,听起来像是一个教科书里的术语,但它的内核其实非常直观:让计算机像人一样,通过观察物体的“形状”来认识它。我们人眼看到一个三角形、一个圆形&… · 2026/9/6 8:43:15

公路落石检测实战:282张VOC+YOLO小数据集训练与部署
公路落石检测实战:282张VOC+YOLO小数据集训练与部署

简介:这是一份面向公路落石场景的目标检测数据集,服务于计算机视觉与智能交通领域的开发者、研究者和算法工程师,用于训练、验证和对比落石识别模型,提升道路监控与自动驾驶的安全预警能力。压缩包共八百四十九个文件,… · 2026/9/26 11:33:51

CTF夺旗赛新手入门:Web、逆向、盲注与Misc实战指南
CTF夺旗赛新手入门:Web、逆向、盲注与Misc实战指南

1. 从零理解CTF夺旗赛:它到底是什么,新手该怎么切入很多人第一次听到“CTF夺旗赛”这个词,脑子里浮现的是两拨人举着旗子互相冲锋的画面。其实CTF(Capture The Flag)在网络安全领域里,指的是一种以解题或攻… · 2026/9/26 11:33:20

蓝印RPA虚拟桌面隔离执行:自动化任务不干扰办公的本地化部署方案
蓝印RPA虚拟桌面隔离执行:自动化任务不干扰办公的本地化部署方案

这次我们来看一个 RPA 工具的新玩法:蓝印 RPA 在虚拟桌面内执行自动化任务。常规思路是 RPA 机器人直接在你正在使用的桌面上操作,结果往往是脚本跑得欢,你手里的活被频繁抢焦点、鼠标乱跳,甚至误点弹窗。蓝印 RPA 的做法是把自动… · 2026/9/26 11:33:02

VS2017下预编译GDAL包配置指南:ABI锁版、避坑与重编译
VS2017下预编译GDAL包配置指南:ABI锁版、避坑与重编译

简介:面向Visual Studio 2017开发者的预编译GDAL库资源包,解决地理空间数据处理中繁琐的编译配置难题。GDAL作为开源地理空间数据抽象库,支持栅格与矢量数据的读写、转换及空间操作,广泛应用于GIS开发、遥感与地图制图领域。资源共… · 2026/9/26 11:33:02

学术版 Codex 配 TaoToken:settings.json 骨架与报错排查指南
学术版 Codex 配 TaoToken:settings.json 骨架与报错排查指南

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

sentrux MCP九大工具API详解:scan、health、evolution、dsm、test_gaps完整参考手册
sentrux MCP九大工具API详解:scan、health、evolution、dsm、test_gaps完整参考手册

sentrux MCP九大工具API详解:scan、health、evolution、dsm、test_gaps完整参考手册 【免费下载链接】sentrux Real-time architectural sensor that helps AI agents close the feedback loop, enabling recursive self-improvement of code quality. Pure Rust. … · 2026/9/26 11:33:02

数据库课后习题答案别硬背:当测试用例集刷,效率翻倍
数据库课后习题答案别硬背:当测试用例集刷,效率翻倍

简介:万常选版《数据库原理与设计》课后习题答案资源,覆盖第2至6章及第9章,适合正在学习关系模型、数据库建模、关系数据理论与模式求精的本科生、自学者作为复习与自测材料。压缩包共7个文件,含3个doc参考答案、2个sql示例脚本、… · 2026/9/26 0:00:21

OpenClaw 替代品?Hermes Agent 踩坑实录:macOS 飞书接入 TaoToken 配置
OpenClaw 替代品?Hermes Agent 踩坑实录:macOS 飞书接入 TaoToken 配置

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

向下兼容与向上兼容:接口设计中的兼容性策略与工程实践
向下兼容与向上兼容:接口设计中的兼容性策略与工程实践

一次版本升级事故,是很多团队绕不过去的坎。线上环境里,服务端明明已经上线了新版接口,老的移动端还在照着旧文档传参数。请求一到网关,校验直接拒绝,用户操作失败,客服群炸了锅,开发群里开始互… · 2026/9/26 0:00:46

了解更多?预约专属演示

我们的顾问将为您一对一讲解产品与方案

企业微信二维码