[请教+讨论] 前端做负载均衡,如何判断 css 成功加载?

2019-05-12 23:57:34 +08:00
 jamblues

一个前端自动切换负载均衡的方案,请教 V 友一下。

有个前端项目,部署了 N 台 CDN,每台机器都分配了独立域名:

cdn1.xxx.com

cdn2.xxx.com

...

步骤一:

进入页面执行前,先判断 Cookie 中是否有 CDN 变量,如果没有,从以上域名中随机挑出一个写入 Cookie 的 CDN 变量中。

步骤二:

加载 css 和 js 的时候,都是通过此类格式加载:

<script>document.write('<link href="'+CDN+'/css.css" />')</script>
<script>document.write('<script src="'+CDN+'/js.js"><\/script>')</script>

步骤三:

页面加载完成后,如果通过判断页面正常加载了,那么就不做任何操作。如果没有正常加载,则可能是该 CDN 挂掉了,然后重新挑 CDN 域名写入 Cookie,然后刷新页面重走步骤一,确保用户看到的是完整的页面。


这样做的好处:

  1. 低成本的负载均衡

  2. 能快速切换有问题机器,对于 DNS 负载均衡,生效有时间局限性。

  3. 机器方面可减少人工运维,降低成本

坏处:

  1. 每个页面都得加入一段冗余的判断

  2. 增加 Cookie 和冗余的脚本浪费 HTTP 传输(当然,Cookie 也可以用 localStorage 解决)

  3. CDN 域名更新起来会稍有些麻烦


那么重点来了,有哪些方法,可以用来判断 JS 或者 CSS 正常加载?

方案一,已投入使用:

加入 cdn.js 文件写入一行:

var CDN_LOADED=1;

引用 cdn.js 后,使用判断

<script>
if(typeof CDN_LOADED =='undefined'){
	// 切换 CDN
}
</script>

因为第一种每次都要多请求一个文件,所以不是特别理想。

引出方案二。

方案二,求大神指导:

<script>document.write('<link rel="stylesheet" type="text/css" href="'+CDN+'/css.css" />')</script>

在样式表后,使用

var styles = document.styleSheets || document.styleSheetList;

if(???) {
   // 切换 CDN
}

这个??? 地方,我试了各种

// 不行,rules 报错
if(styles[0].rules == 0)
// 不行,rules 异常
if(typeof styles[0].rules != 'undefined')
...

也试过 onerror 事件,似乎是没用的。。。

都不行,请问哪位大神可以指点一二?

要求:

P.S. 这种文案实际是可行的,好像应用的企业不多?

4133 次点击
所在节点    程序员
34 条回复
mytry
2019-05-13 10:31:49 +08:00
@jamblues 细节根据自己的业务实现啊
mikoshu
2019-05-13 10:34:07 +08:00
关注一波 我也想知道
Tomorr
2019-05-13 11:11:02 +08:00
根据 fetch 试探一波,再适当做个缓存,应该是好的办法;
在回调里面 append 节点,是可行的;

实际上,我最初想到要做两个功能,另一个是 ping,但是利用 fetch 有跨域的问题,而针对 CDN,一般都支持跨域,所以利用 fetch 试探是可行的
shuax
2019-05-13 11:12:49 +08:00
原来 cdn 是这样用的,我一直用错了。
jamblues
2019-05-13 11:15:47 +08:00
@mytry SW 方案是可行的 但是目前不稳定因素有点多 也无法做到优雅降级 所以…还是非常感谢提供的信息
jamblues
2019-05-13 11:19:42 +08:00
@Tomorr 感谢🙏 之前想过 但是因为不一定是 SPA 应用 会导致每个页面都需要 perfetch (或缓存)。 假设只有 10% 的用户会加载失败 意味着需要牺牲 90% 用户第一次打开的体验
johnnyNg
2019-05-13 11:29:19 +08:00
ajax 请求 css 文本,成功就把文本添加到 style 标签中,失败就换一个 cdn 请求,但是感觉会降低首屏加载速度,可以的话弄一个骨架屏或者正在加载的提示
jamblues
2019-05-13 11:37:05 +08:00
@johnnyNg 是的,这个方案就是方案一。

目前是按这个方式做的,不过不是 fetch css 太费劲了,js 会好一些。

如果能减少这个请求,用其它不知道会不会有更好的体验。
learnshare
2019-05-13 12:34:17 +08:00
前端不负责做负载均衡
mikoshu
2019-05-13 16:31:24 +08:00
var cssnum = document.styleSheets.length;
var ti = setInterval(function() {
if (document.styleSheets.length > cssnum) {
// needs more work when you load a bunch of CSS files quickly
// e.g. loop from cssnum to the new length, looking
// for the document.styleSheets[n].href === url
// ...

// FF changes the length prematurely :()
CSSDone('listening to styleSheets.length change');
clearInterval(ti);

}
}, 10);

这个????
jamblues
2019-05-13 17:26:44 +08:00
@mikoshu

我也在 google 上找到过这个,确实兼容浏览器,

但是这个初衷用来判断 css 是否正常加载,

加上 try , 改改勉强可以用,但效率实在不高...不如方案一了,so...
mikoshu
2019-05-13 17:37:12 +08:00
@jamblues 没看懂方案一,你不是要判断 link 加载的 css 是否加载成功吗
zhengwhizz
2019-05-13 17:42:15 +08:00
方案一就行啊,干嘛这么麻烦绕来绕去,多引一个 js 有什么影响?而且你这个网站一个 js 都没用到?
hailiang88
2019-05-13 19:23:49 +08:00
方案二,看看这段代码有没有帮助
```javascript
function loadCssWithCallback(uri, callback) {
var style = $('<link rel="stylesheet" media="all" href="' + uri + '" type="text/css"/>');
$('head').append(style);

// try to access the css rules
var _canGetNodeRules = function (node) {
var s = node.sheet || node.styleSheet;
try {
// try to load the css rules
var r = s.cssRules;
return true;
} catch (e) {
return false;
}

};

// watch the css loading
var cssLoadWatcher = function (node) {
// when the link element has finished processing it's data, we can access the stylesheet and rules
if (node.sheet || node.styleSheet && _canGetNodeRules(node)) {
if (callback) callback(uri);
} else {
// not yet, let's wait
window.setTimeout(function () {
cssLoadWatcher(node);
}, 10);
}
};

// start watching
cssLoadWatcher(style[0]);
}

```

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/563462

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX