jsDelivr 事故后的公共CDN故障自动切换思考

举报
卢衍飞 发表于 2024/11/20 14:47:22 2024/11/20
【摘要】 12月20号jsDelivr发生了网络事故,jsDelivr失去了大陆ICP牌照,也就是没有大陆节点了。期间中断了大概5个小时,还好我是个人博客,不然就是P0事故了这样的事故其实已经遇到了多次handsome主题就已经遇到多次,今天来回顾一下历史事故公共CDN事故时间线2017年10月15日handsome主题增加对使用到的公共库增加了公共cdn切换的功能,但这个时候没有本地化这些资源git...

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中捕获到了。

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。