jsDelivr 事故后的公共CDN故障自动切换思考
12月20号jsDelivr发生了网络事故,jsDelivr失去了大陆ICP牌照,也就是没有大陆节点了。期间中断了大概5个小时,还好我是个人博客,不然就是P0事故了
这样的事故其实已经遇到了多次handsome主题就已经遇到多次,今天来回顾一下
历史事故
公共CDN事故时间线
2017年10月15日
handsome主题增加对使用到的公共库增加了公共cdn切换的功能,但这个时候没有本地化这些资源git记录
git记录
2018年5月17日
bootcdn 国内某些节点出现故障 故障通知
故障通知
2018年10月1日
那一天bootcdn也发生的故障故障通知
故障通知
2019年2月15日
handsome主题增加对公共库本地化选项故障通知
故障通知
2021年12月20日
jsDelivr公共CDN 发生故障
虽然本地化是一个解决方法,但是无奈没有带宽呐。
现在jsDelivr已经恢复了,该用还是还是要用上,但是有没有一个办法能让它在出现问题的时候自动切换到本地呢?
Service worker 方案
在 jsDelivr 的 china network error 的 issue 里面,看到有人推荐了这个项目:
GitHub - EtherDream/freecdn: A front-end CDN based on ServiceWorker
看了一眼,咦service worker,我不是一直在用的吗,但是这个项目太过庞大,我大致看了一下基本原理就是通过监听fetch event,拦截request,在里面判断当前请求的response是否成功,如果失败,则替换成另一个url接着请求。这样看起来很简单了,但实际上在判断请求是否请求成功这个地方没有那么简单。
一年之前写过了一篇service worker实践的文章 service work 关于更新用户本地缓存的方案
现在在回过来来读发现有两个问题:
首次安装sw的时候,同样会触发controllerchange,导致用户第一次访问博客就会提示「页面缓存更新建议刷新页面」,这个并不合适
sw中fetch的请求,如果访问失败了,只要在cachelist 白名单中就会被缓存,而没有关系请求是否成功(比如404错误),这样会导致即使这个请求后续成功了,因为被缓存了错误的response导致一直显示错误(除非手动更新sw版本号)
第一个问题这里不再详细展开了,其实可以在regsiter的时候判断sw的状态是否是reg.active,如果是说明之前已经安装好sw了,此时如果再出现controllerchange,那么才是需要出现提示框提示用户刷新页面。
第二个问题,判断一个请求是否成功,一开始想着是response.ok 这个返回值是否是true或者是response.status 判断是否是200。但是发现对于非本站点的请求,response.ok 一直是false,response.status 值为0。
后来才发现对于跨域请求的站点,由于安全隐私性,Google不会返回真正状态信息,这叫做opaque filtered response 不透明过滤响应,感兴趣可以戳这里。
这个问题目前来看没什么好办法(如果有好的方法欢迎在评论区告诉我),除非在fetch里面传入{“mode”:“cors”} 表示我们以严格跨域方式进行请求,这个时候目标站点响应的header里面的Access-Control-Allow-Origin 不能是通配符*,而需要指定包含我们的请求方的域名,否则浏览器会提示下面的错误:
The value of the ‘Access-Control-Allow-Origin’ header in the response must not be the wildcard ‘*’ when the request’s credentials mode is ‘include’.
目前改进后的回退逻辑如下,response.status为0,则认为是加载成功了。
return fetch(event.request).then(function(response) {
return caches.open(CACHE_NAME).then(function(cache) {
if (response.status == 0 || response.ok) { //对于响应码为0,暂时无法进一步判断,只能全部认为加载成功
cache.put(event.request, response.clone());
} else {
const new_response = fetchLocal(event);
if (new_response) {
return new_response;
} else {
return response;
}
}
return response;
});
}).
catch(function(error) {
const response = fetchLocal(event);;
if (response) {
return response;
} else {
// console.log(‘Fetching request url ,’ +event.request.url+’ failed:’, error);
// throw error;
}
});
其实一个请求失败分为两种,一种是服务器返回错误的状态码,比如404,502之类,另一种是服务器直接宕机。
第二种请求失败的情况是可以在try 语句的catch中捕获到了。
- 点赞
- 收藏
- 关注作者
评论(0)