當前位置: 妍妍網 > 碼農

前端部署真的不簡單

2024-02-25碼農

現在大部份的中小型公司部署前端程式碼都是比較簡單的,主要步驟如下:

首先,透過腳手架提供的命令 npm run build 打包前端程式碼,生成 dist 資料夾;

最後,將 dist 資料夾丟給後台開發人員放在他們的工程裏面,隨後台一起部署;現在普遍是前後端分開部署,因此,利用 nginx 起一個web伺服器,將 dist 資料夾放到指定的路徑下,配置下 nginx 存取路徑,對於請求介面使用 proxy_pass 進行轉發,解決跨域的問題。

更加高端一點的操作,是利用 CI/CD + Docker 進行自動化部署。

但是,你是否真的想過前端部署真的就這麽簡單嗎?

這其實是一個非常嚴肅且復雜的問題,因為這關系到線上生產環境的穩定

有一天,從自知乎上看到一篇張雲龍大佬在2014年寫的文章,非常有啟發,即使這篇文章距離現在有快10年了,但是其中的思想仍然熠熠生輝。

因為寫的真的是太好了,為了讓更多的人看到,所以大部份內容直接就照搬過來,為了讓自己加深印象。如果想看原文, 原文網址 [1] 在這裏。

那讓我們從原始的前端開發講起。

下圖是一個 index.html 頁面和它的樣式檔 a.css ,無需編譯,本地預覽,丟到伺服器,等待使用者存取。

image.png

哇,前端這麽簡單,門檻好低啊。這也是前端有太多人湧入進來的原因。

接著,我們存取頁面,看到效果,再檢視一下網路請求,200!不錯,太完美了!

image.png

那麽,研發完成。。。。了麽?

等等,這還沒完呢!

對於像 BAT 這種公司來說,那些變態的存取量和效能指標,將會讓前端一點也不好玩。

看看那個 a.css 的請求,如果每次使用者存取頁面都要載入,是不是很影響效能,很浪費頻寬啊,我們希望最好這樣:

image.png

利用 304 ,讓瀏覽器使用本地緩存。

但,這樣也就夠了嗎?

不夠!

304 叫協商緩存,這玩意還是要和伺服器通訊一次,我們的最佳化級別是變態級,所以必須徹底滅掉這個請求,要變成這樣:

image.png

強制瀏覽器使用本地緩存( cache-control/expires ),不要和伺服器通訊。

好了,請求方面的最佳化已經達到變態級別,那問題來了: 你都不讓瀏覽器發資源請求了,這緩存咋更新

很好,相信有人想到了辦法: 透過更新頁面中參照的資源路徑,讓瀏覽器主動放棄緩存,載入新資源

像這樣:

image.png

下次上線,把連結地址改成新的版本,這就更新資源了。

問題解決了麽?當然沒有,思考這種情況:

image.png

頁面參照了3個 css 檔,而某次上線只改了其中的 a.css ,如果所有連結都更新版本,就會導致 b.css c.css 的緩存也失效,那豈不是又有浪費了?

不難發現,要解決這種問題, 必須讓url的修改與檔內容關聯,也就是說,只有檔內容變化,才會導致相應url的變更,從而實作檔級別的精確緩存控制

什麽東西與檔內容相關呢?

我們會很自然的聯想到利用 數據摘要要演算法 對檔求摘要資訊,摘要資訊與檔內容一一對應,就有了一種可以精確到單個檔粒度的緩存控制依據了。

OK,那我們把 url 改成帶摘要資訊的:

image.png

這回再有檔修改,就只更新那個檔對應的 url 了,想到這裏貌似很完美了。你覺得這就夠了麽?

圖樣圖森破!

現代互聯網企業,為了進一步提升網站效能,會把 靜態資源和動態網頁分集群部署 ,靜態資源會被部署到 CDN 節點上,網頁中參照的資源也會變成對應的部署路徑:

image.png

好了,當我要更新靜態資源的時候,同時也會更新 html 中的參照吧,就好像這樣:

image.png

這次釋出,同時改了頁面結構和樣式,也更新了靜態資源對應的url地址。現在重點來了,現在要釋出程式碼上線,親愛的前端研發同學,你來告訴我, 咱們是先上線頁面,還是先上線靜態資源

這裏的靜態資源不僅僅包括css檔,也包括圖片,以及不怎麽經常變的資源。

  1. 先部署動態頁面,再部署靜態資源 :在二者部署的時間間隔內,如果有使用者存取頁面,就會在新的頁面結構中載入舊的資源,並且把這個舊版本的資源當做新版本緩存起來,其結果就是:使用者存取到了一個樣式錯亂的頁面,除非手動重新整理,否則在資源緩存過期之前,頁面會一直執行錯誤。

  2. 先部署靜態資源,再部署動態頁面 :在部署時間間隔之內,有舊版本資源本地緩存的使用者存取網站,由於請求的頁面是舊版本的,資源參照沒有改變,瀏覽器將直接使用本地緩存,這種情況下頁面展現正常;但沒有本地緩存或者緩存過期的使用者存取網站,就會出現舊版本頁面載入新版本資源的情況,導致頁面執行錯誤,但當頁面完成部署,這部份使用者再次存取頁面又會恢復正常了。

好的,上面一坨分析想說的就是:先部署誰都不成!都會導致部署過程中發生頁面錯亂的問題。

所以, 存取量不大的計畫,可以讓研發同學苦逼一把,等到半夜偷偷上線,先上靜態資源,再部署頁面,看起來問題少一些 。這也是很多公司的部署方案。

但是,大公司超變態,沒有這樣的絕對低峰期,只有相對低峰期。

所以,為了穩定的服務,還得繼續追求極致啊!

這個奇葩問題,起源於資源的 覆蓋式釋出 ,用待釋出資源覆蓋已釋出資源,就有這種問題。

解決它也好辦,就是實作 非覆蓋式釋出

image.png

看上圖,用檔的摘要資訊來對資原始檔進行重新命名,把摘要資訊放到資原始檔釋出路徑中,這樣,內容有修改的資源就變成了一個新的檔釋出到線上,不會覆蓋已有的資原始檔。上線過程中, 先全量部署靜態資源,再灰度部署頁面 ,整個問題就比較完美的解決了。

因為很多前端開發同學不怎麽接觸部署,對灰度部署不太熟悉,下面將介紹下什麽是灰度部署。

軟體開發一般都是一個版本一個版本的叠代。新版本上線前都會經過測試,但就算這樣,也不能保證上線了不出問題。

所以,在公司裏上線新版本程式碼一般都是透過灰度系統。灰度系統可以把流量劃分成多份,一份走新版本程式碼,一份走老版本程式碼。

image.png

而且灰度系統支持設定流量的比例,比如可以把走新版本程式碼的流程設定為 5%,沒啥問題了再放到 10%,50%,最後放到 100% 全量。這樣可以把出現問題的影響降到最低。

不然一上來就全量,萬一出了線上問題,那就是大事故。

另外,灰度系統不止這一個用途,比如,產品不確定某些改動是不是有效的,就要做 AB 實驗,也就是要把流量分成兩份,一份走 A 版本程式碼,一份走 B 版本程式碼。

那這樣的灰度系統是怎麽實作的呢?其實很多都是用 nginx 實作的。

nginx 是一個反向代理的服務,使用者請求發給它,由它轉發給具體的套用伺服器。

image.png

它的過程如下圖所示:

image.png

首先,需要對流量進行染色,即對這個使用者進行標註,讓這個使用者存取服務1,另外的使用者存取服務2。染色的方式有很多,可以透過 cookie 來完成。不同的使用者攜帶的 cookie 是不同的。第一染色的時候,所有的使用者都存取服務1。

然後,第二次存取的時候, nginx 根據使用者攜帶的 cookie 進行轉發到不同的服務,這樣就完成了灰度存取。

好了,灰度部署就介紹到這裏,回到原文講的 先全量部署靜態資源,再灰度部署頁面 ,這是什麽意思呢?

首先,部署靜態資源的時候,不要刪除原來的靜態資源,而是把新的靜態資源發復制過去,因為檔名用摘要演算法重新命名的,所以不會發生重名的問題。

其次,灰度部署動態頁面,也就是一部份使用者存取老的頁面,一部份使用者存取新的頁面。存取老頁面的使用者請求的還是老資源,直接使用緩存。存取新頁面的使用者存取新資源,此時新資源已經部署完成,所以不會存取老的資源,導致頁面出現錯誤。

最後,根據存取情況,利用灰度系統,逐漸把存取老頁面的使用者過渡到存取新頁面上。

所以,大公司的靜態資源最佳化方案,基本上要實作這麽幾個東西:

  1. 配置超長時間的本地緩存:節省頻寬,提高效能

  2. 采用內容摘要作為緩存更新依據:精確的緩存控制

  3. 靜態資源CDN部署:最佳化網路請求

  4. 更資源釋出路徑實作非覆蓋式釋出:平滑升級

全套做下來,就是相對比較完整的靜態資源緩存控制方案了,而且,還要註意的是,靜態資源的緩存控制要求在 前端所有靜態資源載入的位置都要做這樣的處理

是的,所有!

什麽js、css自不必說,還要包括js、css檔中參照的資源路徑,由於涉及到摘要資訊,參照資源的摘要資訊也會引起參照檔本身的內容改變,從而形成級聯的摘要變化,大概就是:

image.png

到這裏本文結束了,我們已經了解了前端部署中關於靜態資源緩存要面臨的最佳化和部署問題,新的問題又來了:這™讓工程師怎麽寫碼啊!!!

這又會扯出一堆有關模組化開發、資源載入、請求合並、前端框架等等的工程問題。

媽媽,我再也不玩前端了。。。。

原文: https://juejin.cn/post/7316202725330796571 作者:小p 參考資料

[1]

https://www.zhihu.com/question/20790576