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