/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */
#content_views .toc,
/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */
#content_views.markdown_views > p:empty:has(+ .toc),
#content_views.markdown_views > .toc + p:empty,
/* 富文本旧版目录标记 */
#content_views.htmledit_views #main-toc,
#content_views.htmledit_views #hr-toc,
#content_views.htmledit_views p[id*=\”-toc\”] {
display: none !important;
}
/* 目录去掉后,紧跟的首个标题不再多出一块上边距 */
#content_views.markdown_views > .toc + h1,
#content_views.markdown_views > .toc + h2,
#content_views.markdown_views > .toc + h3,
#content_views.markdown_views > .toc + h4,
#content_views.markdown_views > .toc + p:empty + h1,
#content_views.markdown_views > .toc + p:empty + h2,
#content_views.markdown_views > .toc + p:empty + h3,
#content_views.markdown_views > .toc + p:empty + h4 {
margin-top: 0 !important;
}
前端高可用:CDN 降级与静态资源容灾方案

在大型互联网产品的高可用架构中,人们常常把精力聚焦在后端的熔断限流、数据库主从切换与多活机房上。然而,对于现代基于单页面应用(SPA)和微前端架构的前端工程而言,最脆弱、最致命的单点故障往往出在 CDN 静态资源加载上。
一旦主 CDN 厂商发生节点故障、DNS 污染或跨运营商链路抖动,用户的浏览器在请求 app.4f82a.js 或 chunk-vendor.js 时返回 404/503 或超时,整座系统将瞬间沦为“全网白屏”。在这种情况下,后端集群无论多么健康强悍,用户根本连页面骨架都进不来。
构建企业级前端高可用体系,必须建立**“多 CDN 动态容灾切换”、“静态资源重试探针”与“Service Worker 本地离线兜底”**三位一体的纵深防御体系。
静态资源加载失败的三级容灾防御网
flowchart TD
Req[浏览器请求静态资源 script/css] –> PrimaryCDN[主 CDN 节点 (Aliyun/Tencent)]
PrimaryCDN –>|正常返回 200| Success[正常解析执行]
PrimaryCDN –>|网络超时 / 404 / 503 失败| L1[第一级防御: 脚本错误捕获探针 (window.addEventListener 'error')]
L1 –> BackupCDN[第二级防御: 动态替换为备用 CDN 域名并增量重试]
BackupCDN –>|备用 CDN 加载成功| Success
BackupCDN –>|全部 CDN 瘫痪| L2[第三级防御: Service Worker 本地持久化缓存降级激活]
L2 –> LocalCache[从 IndexedDB/CacheStorage 恢复历史安全版本]
第一级防御:内联轻量级重试探针(Retry Loader Script)
静态资源加载错误属于资源层错误,不会冒泡到 window.onerror,但可以通过捕获阶段的 window.addEventListener('error', callback, true) 捕获到。
在 HTML <head> 中内联一段极小(< 2KB)的高可用兜底探针,当检测到 <script> 或 <link> 标签加载失败时,自动重定向至备用 CDN 域名重新拉取:
<script>
(function() {
var primaryDomain = 'static.maincdn.com';
var backupDomains = ['static.backupcdn.com', 'static-fallback.oss-internal.com'];
var maxRetryCount = 2;
window.addEventListener('error', function(e) {
var target = e.target;
if (!target || !(target.tagName === 'SCRIPT' || target.tagName === 'LINK')) return;
var url = target.src || target.href;
if (!url || url.indexOf(primaryDomain) === -1) return;
var retryCount = parseInt(target.getAttribute('data-retry') || '0', 10);
if (retryCount >= backupDomains.length) {
console.error('[高可用报警] 静态资源所有备用 CDN 均加载失败:', url);
return;
}
var nextDomain = backupDomains[retryCount];
var newUrl = url.replace(primaryDomain, nextDomain);
console.warn('[CDN 容灾触发] 资源加载失败,正在切换至备用 CDN 重试:', newUrl);
if (target.tagName === 'SCRIPT') {
var newScript = document.createElement('script');
newScript.src = newUrl;
newScript.setAttribute('data-retry', retryCount + 1);
newScript.crossOrigin = target.crossOrigin;
document.head.appendChild(newScript);
} else if (target.tagName === 'LINK') {
var newLink = document.createElement('link');
newLink.rel = target.rel;
newLink.href = newUrl;
newLink.setAttribute('data-retry', retryCount + 1);
document.head.appendChild(newLink);
}
}, true);
})();
</script>
第二级防御:多 CDN 动态 DNS 智能解析与灰度分发
在企业网关层,采用智能 DNS 路由调度(Anycast DNS):
- 对不同地区、不同运营商(电信/联通/移动/海外)的用户动态分配最优 CDN 节点;
- 配合心跳健康监测:一旦主 CDN 厂商的可用率在 1 分钟内跌破 99%,网关 DNS 自动无缝秒级平移将全量流量切至备用 CDN,整个过程对业务无感知。
第三级防御:Service Worker 离线版本兜底
对于核心中台或重度生产力工具,借助 Service Worker 与 Cache Storage 建立“陈旧但可用(Stale-While-Revalidate)”的离线缓存:
- 每次用户成功访问应用,Service Worker 在后台将当前版本的核心 JS/CSS 离线缓存;
- 当某天遇到灾难性的全网 CDN 故障时,Service Worker 拦截失败请求,自动返回上一次成功缓存的离线安全版本,并弹出“当前处于离线容灾模式”提示,保证核心业务读写不中断。
// sw.js 离线容灾策略核心
self.addEventListener('fetch', (event: any) => {
const request = event.request;
if (request.destination === 'script' || request.destination === 'style') {
event.respondWith(
fetch(request)
.then(response => {
if (response.ok) {
const copy = response.clone();
caches.open('static-v1').then(cache => cache.put(request, copy));
}
return response;
})
.catch(async () => {
// 网络失败时,自动降级命中本地缓存
const cached = await caches.match(request);
if (cached) {
console.warn('[Service Worker] 网络异常,已从本地离线缓存安全降级恢复:', request.url);
return cached;
}
throw new Error('离线缓存未命中且网络不可达');
})
);
}
});
把高可用做到极致,必须将极端故障常态化预设。用严密的多层重试与离线缓存网格,牢牢守护前端用户体验的最后一道生命线。

