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