CDN / 静态资源故障容灾
场景
CDN 服务故障或某个静态资源加载失败,导致页面白屏或功能异常。如何设计容灾方案?
面试速答版
CDN / 静态资源故障怎么容灾? 捕获阶段监听资源错误 + 多 CDN 自动切换 + Service Worker 兜底:
window.addEventListener('error', fn, true)捕获 script/link/img 加载失败(资源错误不冒泡)。- 多 CDN 列表轮询:失败自动切下一个域名,直到成功或全部失败。
- 关键脚本(如登录、支付)静态内联兜底版本,CDN 全挂也能跑。
- Service Worker 缓存上一次成功版本,CDN 故障时离线兜底。
- 监控上报失败的 CDN 节点,方便运维定位区域性问题。
分析思路
资源加载失败的检测
全局资源错误监听
window.addEventListener('error', (e) => {
const target = e.target as HTMLElement;
if (target?.tagName) {
const src = (target as HTMLScriptElement).src
|| (target as HTMLLinkElement).href
|| (target as HTMLImageElement).src;
console.error(`资源加载失败: ${target.tagName} ${src}`);
handleResourceError(target.tagName, src);
}
}, true); // 捕获阶段
容灾方案
方案 1:多 CDN 备份切换
script 加载失败自动切换 CDN
const CDN_LIST = [
'https://cdn1.example.com',
'https://cdn2.example.com',
'https://cdn3.example.com',
];
function loadScriptWithFallback(path: string, cdnIndex = 0): Promise<void> {
if (cdnIndex >= CDN_LIST.length) {
return Promise.reject(new Error(`所有 CDN 均不可用: ${path}`));
}
return new Promise((resolve, reject) => {
const script = document.createElement('script');
script.src = `${CDN_LIST[cdnIndex]}${path}`;
script.onload = () => resolve();
script.onerror = () => {
console.warn(`CDN ${cdnIndex} 失败,尝试 CDN ${cdnIndex + 1}`);
loadScriptWithFallback(path, cdnIndex + 1).then(resolve, reject);
};
document.head.appendChild(script);
});
}
方案 2:Service Worker 缓存兜底
sw.ts — 缓存关键资源
const CACHE_NAME = 'app-v1';
const CRITICAL_ASSETS = ['/index.html', '/main.js', '/style.css'];
self.addEventListener('install', (event: ExtendableEvent) => {
event.waitUntil(
caches.open(CACHE_NAME).then((cache) => cache.addAll(CRITICAL_ASSETS))
);
});
self.addEventListener('fetch', (event: FetchEvent) => {
event.respondWith(
fetch(event.request).catch(() => {
// 网络失败时从缓存读取
return caches.match(event.request).then((cached) => {
return cached || new Response('资源不可用', { status: 503 });
});
})
);
});
方案 3:HTML 内联关键资源
关键 CSS 内联到 HTML
<head>
<!-- 关键 CSS 内联,不依赖外部资源 -->
<style>
body { margin: 0; font-family: system-ui; }
.loading { display: flex; justify-content: center; align-items: center; height: 100vh; }
</style>
</head>
常见面试问题
Q1: CDN 挂了怎么办?
答案:
- 检测:
window.addEventListener('error')捕获资源加载失败 - 自动切换:多 CDN 备份,失败时自动切换域名
- 缓存兜底:Service Worker 缓存关键资源
- 降级方案:CDN 全挂时展示本地缓存或静态兜底页面
- 监控告警:CDN 资源加载失败率超阈值自动告警