摘要:署名國(guó)際本文作者蘇洋創(chuàng)建時(shí)間年月日統(tǒng)計(jì)字?jǐn)?shù)字閱讀時(shí)間分鐘閱讀本文鏈接簡(jiǎn)單策略讓前端資源實(shí)現(xiàn)高可用前幾天有朋友問我,曾經(jīng)在前公司里使用過的前端資源高可用方案是怎么做的。
本文使用「署名 4.0 國(guó)際 (CC BY 4.0)」許可協(xié)議,歡迎轉(zhuǎn)載、或重新修改使用,但需要注明來源。 署名 4.0 國(guó)際 (CC BY 4.0)
本文作者: 蘇洋
創(chuàng)建時(shí)間: 2019年05月14日 統(tǒng)計(jì)字?jǐn)?shù): 6024字 閱讀時(shí)間: 13分鐘閱讀 本文鏈接: soulteary.com/2019/05/14/…
前幾天有朋友問我,曾經(jīng)在前公司里使用過的前端資源高可用方案是怎么做的。資源高可用聽起來應(yīng)該是后端、運(yùn)維同學(xué)的“分內(nèi)之事”。但是前端資源的高可用并沒有那么簡(jiǎn)單,在當(dāng)前復(fù)雜的網(wǎng)絡(luò)環(huán)境下,你是指望用戶多刷新幾次、還是期望用戶把Wi-Fi切換為4G,撞大運(yùn)解決問題?獲客成本如此之高的今天,放棄用戶是不明智的。
想到許久沒有寫前端相關(guān)的文章了,決定在這里簡(jiǎn)單聊聊。希望能幫助到創(chuàng)業(yè)階段的公司和團(tuán)隊(duì)。
在聊技術(shù)細(xì)節(jié)之前,我們先聊聊“什么是前端資源高可用”。
資源高可用和前端有什么關(guān)系?前端資源高可用這個(gè)需求,對(duì)于“大廠”的同學(xué)來說應(yīng)該很陌生。
因?yàn)閷?duì)于大公司來說,有大量冗余的云主機(jī)資源可以為業(yè)務(wù)團(tuán)隊(duì)提供,并且會(huì)配套一定規(guī)模的運(yùn)維團(tuán)隊(duì)。當(dāng)監(jiān)控系統(tǒng)發(fā)現(xiàn)線上出現(xiàn)資源不可用的情況時(shí),系統(tǒng)能夠根據(jù)策略自動(dòng)切換問題資源到備份資源,而有些不能自動(dòng)切換的服務(wù),則會(huì)有值守的運(yùn)維同學(xué),在第一時(shí)間手動(dòng)進(jìn)行切換,保障業(yè)務(wù)的高可用。
而小一些規(guī)模的創(chuàng)業(yè)公司就沒那么幸運(yùn)了,資源相對(duì)緊張,甚至沒有完善的監(jiān)控措施,更別提配一只相對(duì)完善的運(yùn)維團(tuán)隊(duì)了。
或許會(huì)有人認(rèn)為,將靜態(tài)資源扔到 CDN 上后就一勞永逸了。然而現(xiàn)實(shí)世界中,網(wǎng)絡(luò)環(huán)境十分復(fù)雜,相同主機(jī)在不同線路、不同地區(qū)、不同時(shí)間段的可用性和訪問質(zhì)量是不同的,所以使用 CDN 不是解決這個(gè)問題的銀彈,但是同時(shí)使用多個(gè) CDN 或許是當(dāng)前階段比較通用的方案。
比如默認(rèn)不同地域的用戶通過不同線路訪問網(wǎng)站,如果其中一條線路出現(xiàn)問題,那么一部分用戶就無法訪問網(wǎng)站提供的服務(wù)。
這個(gè)時(shí)候,我們通常會(huì)使用切換請(qǐng)求資源服務(wù)器的方法來解決問題,比如下面這樣。
當(dāng)某條 CDN / 服務(wù)線路不正常的時(shí)候,我們可以通過切換域名來解決資源獲取不到的問題,但是別忘記一件很重要的事情:
域名生效需要時(shí)間、多地域生效周期漫長(zhǎng),在這個(gè)切換域名的時(shí)間窗口內(nèi),你的服務(wù)質(zhì)量將會(huì)持續(xù)受到影響。
并且這個(gè)方案的資源切換動(dòng)作通常會(huì)在后端進(jìn)行,而此時(shí)頁(yè)面已經(jīng)推送到用戶側(cè),資源已經(jīng)不可用,用戶需要刷新后才有可能請(qǐng)求到新的資源地址,并且是在 DNS 能夠生效的前提下,我們知道很多流行的應(yīng)用客戶端為了性能優(yōu)化,都為資源(甚至包含頁(yè)面)設(shè)置了很長(zhǎng)的有效期,可以說這個(gè)方案并不是一個(gè)很有效的方案。
所以,假設(shè)你采取類似這種方案,你必須確保下面四個(gè)條件都生效,才能達(dá)到你的目的:
你的監(jiān)控系統(tǒng)發(fā)現(xiàn)了問題,并自動(dòng)進(jìn)行了資源切換。
你的業(yè)務(wù)負(fù)責(zé)人,發(fā)現(xiàn)了問題,并手動(dòng)進(jìn)行資源切換。
你成功切換了資源,并且 DNS 快速生效(網(wǎng)絡(luò)層、客戶端層)。
你的用戶在你切換資源、DNS 生效后,恰如其分的刷新了頁(yè)面,而不是直接離開。
聽起來是不是很魔幻。
那么有沒有什么簡(jiǎn)單可靠的方案可以解決這個(gè)問題呢?
有,讓資源在前端層面進(jìn)行自動(dòng)切換。
方案簡(jiǎn)介通過在前端環(huán)境監(jiān)聽資源加載錯(cuò)誤信息,并根據(jù)一定策略自動(dòng)加載其他位置的資源,實(shí)現(xiàn)前端依賴的資源在前端(用戶側(cè))進(jìn)行自動(dòng)切換,達(dá)到前端資源高可用的目的,減少因前端資源加載失敗而導(dǎo)致的服務(wù)不可用和用戶流失。
環(huán)境模擬為了更直觀的演示方案如何生效,我這里使用 Docker 做一個(gè)常見場(chǎng)景的模擬。
模擬多個(gè)網(wǎng)絡(luò)我們先創(chuàng)建一個(gè) docker-compose.yml ,里面包含下面的內(nèi)容。
version: "3"
services:
web:
image: ${NGX_IMAGE}
4 expose:
- 80
networks:
- traefik
labels:
- "traefik.enable=true"
- "traefik.frontend.rule=Host:${MAIN_HOST}"
- "traefik.frontend.entryPoints=${SUPPORT_PROTOCOL}"
volumes:
- ./public/${MAIN_HOST}:/usr/share/nginx/html
extra_hosts:
- "${MAIN_HOST}:127.0.0.1"
cdn1:
image: ${NGX_IMAGE}
expose:
- 80
networks:
- traefik
labels:
- "traefik.enable=true"
- "traefik.frontend.rule=Host:${CDN_HOST1}"
- "traefik.frontend.entryPoints=${SUPPORT_PROTOCOL}"
- "traefik.frontend.headers.customResponseHeaders=Access-Control-Allow-Origin:*"
volumes:
- ./public/${CDN_HOST1}:/usr/share/nginx/html
extra_hosts:
- "${CDN_HOST1}:127.0.0.1"
cdn2:
image: ${NGX_IMAGE}
expose:
- 80
networks:
- traefik
labels:
- "traefik.enable=true"
- "traefik.frontend.rule=Host:${CDN_HOST2}"
- "traefik.frontend.entryPoints=${SUPPORT_PROTOCOL}"
- "traefik.frontend.headers.customResponseHeaders=Access-Control-Allow-Origin:*"
volumes:
- ./public/${CDN_HOST2}:/usr/share/nginx/html
extra_hosts:
- "${CDN_HOST2}:127.0.0.1"
networks:
traefik:
external: true
可以看到,編排文件里面定義了一個(gè)應(yīng)用網(wǎng)站,和兩個(gè) CDN 服務(wù),為了更接近真實(shí)場(chǎng)景。其中一個(gè) CDN 和應(yīng)用網(wǎng)站根域名相同、另外一個(gè)采取完全不同的域名,比如下面這樣。
# 默認(rèn)使用的鏡像
NGX_IMAGE=nginx:1.15.8-alpine
# 支持訪問的協(xié)議
SUPPORT_PROTOCOL=https,http
# 主站點(diǎn)的域名
MAIN_HOST=demo.lab.io
# 模擬根域名相同的CDN
CDN_HOST1=demo-cdn.lab.io
# 模擬根域名不同的CDN
CDN_HOST2=demo.cdn2.io
將上面的內(nèi)容保存為 .env ,并將上面內(nèi)容中的域名綁定到本地之后,執(zhí)行 docker-compose up ,就可以開始實(shí)戰(zhàn)了。
模擬常規(guī)場(chǎng)景執(zhí)行 docker-compose up 之后,我們會(huì)看到 Docker 自動(dòng)幫我們創(chuàng)建了幾個(gè)目錄。
./public ├── demo-cdn.lab.io ├── demo.cdn2.io └── demo.lab.io
我們?cè)?demo.lab.io 目錄下創(chuàng)建 index.html 文件,作為應(yīng)用入口。
"en">
"UTF-8">
"viewport" content="width=device-width, initial-scale=1.0">
"X-UA-Compatible" content="ie=edge">
Document
然后在 ./demo.lab.io/public/assets/app.js 創(chuàng)建一個(gè)腳本文件,隨便寫點(diǎn)什么,模擬被加載的資源。
document.addEventListener("DOMContentLoaded", function () {
var p = document.createElement("p");
p.innerText = "script excute success.";
document.body.appendChild(p);
});
當(dāng)我們?cè)L問 http://demo.lab.io/index.html 的時(shí)候,不出意外,將會(huì)看到 由腳本輸出的 script excute success. 內(nèi)容。
我們將 ./public/demo.lab.io/assets/app.js 復(fù)制到 ./public/demo-cdn.lab.io/assets/app.js 和 ./public/demo.cdn2.io/assets/app.js 中,模擬資源分發(fā)到 CDN 的場(chǎng)景。
最簡(jiǎn)單的技術(shù)實(shí)現(xiàn)先將上面請(qǐng)求的資源地址修改為“CDN”的地址,驗(yàn)證一下“CDN”服務(wù)是否可用。
"en">
"UTF-8">
"viewport" content="width=device-width, initial-scale=1.0">
"X-UA-Compatible" content="ie=edge">
Document
然后通過刪除 ./public/demo-cdn.lab.io/assets/app.js 這個(gè)腳本,模擬 CDN 資源失效的場(chǎng)景。
如果你的瀏覽器沒有奇怪的緩存行為,你將會(huì)得到一個(gè)空白的頁(yè)面,以及一行報(bào)錯(cuò)信息:
default.html:8 GET http://demo-cdn.lab.io/assets/app.js 404 (Not Found)
如果碰到域名解析錯(cuò)誤的場(chǎng)景下,我們會(huì)獲得另外一種錯(cuò)誤信息:
GET http://demo-cdn.lab.io/assets/app.js net::ERR_NAME_NOT_RESOLVED
這個(gè)時(shí)候,我們可以在頁(yè)面上做一些修改,讓它能夠在資源加載出錯(cuò)的時(shí)候,將資源切換到另外一個(gè) CDN 資源上,比如這樣:
"en">
"UTF-8">
"viewport" content="width=device-width, initial-scale=1.0">
"X-UA-Compatible" content="ie=edge">
Document
再次打開地址,你會(huì)發(fā)現(xiàn)頁(yè)面又正常了。
進(jìn)階版本上面場(chǎng)景,我們模擬了常規(guī)場(chǎng)景下前端自動(dòng)切換資源的方式。
接下來我們來做一些小小的優(yōu)化,讓腳本加載支持更多的資源地址,達(dá)到更高的可用性。
"en">
"UTF-8">
"viewport" content="width=device-width, initial-scale=1.0">
"X-UA-Compatible" content="ie=edge">
Document
上面的實(shí)現(xiàn)中,我們將資源加載寫的更加通用,并且添加了加載成功、失敗的回調(diào),以及額外做了一個(gè)自動(dòng)切換資源的函數(shù),并將頁(yè)面腳本資源加載交給了腳本去處理。
這個(gè)方案已經(jīng)能夠解決多數(shù)場(chǎng)景下的問題了,但是如果你的資源之間存在依賴關(guān)系,又該怎么處理呢?
結(jié)合資源加載器使用我們以 AMD 模塊規(guī)范為例,聊聊如何結(jié)合 requirejs 使用資源自動(dòng)切換。
"en">
"UTF-8">
"viewport" content="width=device-width, initial-scale=1.0">
"X-UA-Compatible" content="ie=edge">
Document
將 requirejs 引入頁(yè)面,然后使用 requirejs 方法替換 loadResource 方法后,你會(huì)發(fā)現(xiàn)似乎一切沒有什么不同。
但是你其實(shí)可以通過配置 requirejs.config 來讓資源在加載的過程中,將依賴資源先進(jìn)行下載和初始化,舉兩個(gè)實(shí)際的例子:
requirejs.config({
map: {
// 這是一個(gè) hack 用法,具體含義參考官方 API 文檔
"*": { "http://demo.cdn2.io/assets/app.js": "lodash" },
}
});
requirejs.config({
shim:{
// 或者這樣聲明
"http://demo.cdn2.io/assets/app.js":{
deps:["vue"]
}
}
});
當(dāng)然,你也可以改造 autoSwitch 函數(shù),自己動(dòng)態(tài)維護(hù)依賴關(guān)聯(lián)。
其他的坑講到這里,資源自動(dòng)加載幾乎講完了,但是實(shí)際上還存在一些額外的坑。
比如結(jié)合當(dāng)前最流行的構(gòu)建工具 webpack 使用,圖片資源是一次性寫死的,需要支持動(dòng)態(tài)化。
17年的時(shí)候,我曾經(jīng)提交了一個(gè)解決方案,有興趣的同學(xué)可以圍觀一下:github.com/soulteary/w…,主要解決了 ** Not generating ouput with multiple entries** 的問題。
最后許多看似高大上的方案,本質(zhì)其實(shí)都十分簡(jiǎn)單。與其追求高大上的概念,不如靜下心來,踏實(shí)鉆研細(xì)節(jié),思考技術(shù)到底該如何有效的服務(wù)業(yè)務(wù)、產(chǎn)生價(jià)值。
—EOF
我現(xiàn)在有一個(gè)小小的折騰群,里面聚集了一些喜歡折騰的小伙伴。
在不發(fā)廣告的情況下,我們?cè)诶锩鏁?huì)一起聊聊軟件、HomeLab、編程上的一些問題,也會(huì)在群里不定期的分享一些技術(shù)沙龍的資料。
喜歡折騰的小伙伴歡迎掃碼添加好友。(請(qǐng)注明來源和目的,否則不會(huì)通過審核)
關(guān)于折騰群入群的那些事
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://www.ezyhdfw.cn/yun/7277.html
摘要:接入層作用一的聚合。接入層作用二服務(wù)發(fā)現(xiàn)與動(dòng)態(tài)負(fù)載均衡既然統(tǒng)一的入口變?yōu)榱私尤雽?,則接入層就有責(zé)任自動(dòng)的發(fā)現(xiàn)后端拆分,聚合,擴(kuò)容,縮容的服務(wù)集群,當(dāng)后端服務(wù)有所變化的時(shí)候,能夠?qū)崿F(xiàn)健康檢查和動(dòng)態(tài)的負(fù)載均衡。 此文已由作者劉超授權(quán)網(wǎng)易云社區(qū)發(fā)布。 歡迎訪問網(wǎng)易云社區(qū),了解更多網(wǎng)易技術(shù)產(chǎn)品運(yùn)營(yíng)經(jīng)驗(yàn)。 這個(gè)系列是微服務(wù)高并發(fā)設(shè)計(jì),所以我們先從最外層的接入層入手,看都有什么樣的策略保證高并發(fā)。...
摘要:淺談秒殺系統(tǒng)架構(gòu)設(shè)計(jì)后端掘金秒殺是電子商務(wù)網(wǎng)站常見的一種營(yíng)銷手段。這兩個(gè)項(xiàng)目白話網(wǎng)站架構(gòu)演進(jìn)后端掘金這是白話系列的文章。 淺談秒殺系統(tǒng)架構(gòu)設(shè)計(jì) - 后端 - 掘金秒殺是電子商務(wù)網(wǎng)站常見的一種營(yíng)銷手段。 不要整個(gè)系統(tǒng)宕機(jī)。 即使系統(tǒng)故障,也不要將錯(cuò)誤數(shù)據(jù)展示出來。 盡量保持公平公正。 實(shí)現(xiàn)效果 秒殺開始前,搶購(gòu)按鈕為活動(dòng)未開始。 秒殺開始時(shí),搶購(gòu)按鈕可以點(diǎn)擊下單。 秒殺結(jié)束后,按鈕按鈕變...
摘要:微信紅包的核心點(diǎn)是搖,拆,分享紅包,整個(gè)系統(tǒng)設(shè)計(jì)時(shí)必須盡最大可能保證這三個(gè)步驟一氣呵成,任何關(guān)聯(lián)系統(tǒng)出現(xiàn)異常的時(shí)候馬上進(jìn)行系統(tǒng)降級(jí),防止引起系統(tǒng)雪崩。事實(shí)上,真正支撐起微信紅包順暢運(yùn)營(yíng)的幕后英雄,正是騰訊內(nèi)部一個(gè)叫做海量之道的技術(shù)體系。 showImg(https://segmentfault.com/img/bVtam6); 編者按: 微信這么大的流量,尤其是瞬間的峰值,對(duì)于任何團(tuán)隊(duì)...
閱讀 2107·2021-11-15 11:39
閱讀 3278·2021-10-09 09:41
閱讀 1556·2019-08-30 14:20
閱讀 3361·2019-08-30 13:53
閱讀 3377·2019-08-29 16:32
閱讀 3506·2019-08-29 11:20
閱讀 3080·2019-08-26 13:53
閱讀 829·2019-08-26 12:18