天天看點

炫技,從 12.67s 到 1.06s 的網站性能優化實戰

0.引言

作為網際網路項目,最重要的便是使用者體驗。在舉國“網際網路+”的熱潮中,使用者至上也已經被大多數企業所接收,特别是在如今移動端快速發展的時代,我們的網頁不僅隻是呈現在使用者的PC浏覽器裡,更多的時候,使用者是通過移動産品浏覽我們的網頁。加之有越來越多的開發者投入到Web APP和Hybrid APP的開發隊伍中,性能這一問題又再一次被提上了程式員們重點關注的要素。我曾經看到過這樣一句話:一個網站的體驗,決定了使用者是否願意去了解網站的功能;而網站的功能,決定了使用者是否會一票否決網站的體驗。這是改版自網絡上的一句流行語,但卻把網站性能這件事說的十分透徹,特别是在網站這樣的項目中,如果一個使用者需要超過5s才能看見頁面,他會毫不猶豫地關閉它。性能優化,作為工程師界的“上乘武功”,是我們在開發中老生常談的話題,也是一名開發者從入門向資深進階的必經階段,雖然我們看到過很多的标準、軍規,但在真正實踐中,卻常常力不從心,不知道落下了什麼,不知道性能是否還有進一步優化的空間。

對于網站的性能,在行業内有很多既定的名額,但就以前端er而言,我們應該更加關注以下名額:白屏時間、首屏時間、整頁時間、DNS時間、CPU占用率。而我之前自己搭建的一個網站(網址:jerryonlyzrj.com/resume/ ,近日因域名備案無法打開,幾日後即恢複正常),完全沒做性能優化時,首屏時間是12.67s,最後經過多方面優化,終于将其降低至1.06s,并且還未配置CDN加速。其中過程我踩了很多坑,也翻了許多專業書籍,最後決定将這幾日的努力整理成文,幫助前端愛好者們少走彎路。文章更新可能之後不會實時同步在論壇上,歡迎大家關注我的Github,我會把最新的文章更新在對應的項目裡,讓我們一起在代碼的海洋裡策馬奔騰:github.com/jerryOnlyZR… 。

今天,我們将從性能優化的三大方面工作逐漸展開介紹,其中包括網絡傳輸性能、頁面渲染性能以及JS阻塞性能,系統性地帶着讀者們體驗性能優化的實踐流程。

1.網絡傳輸性能優化

在開始介紹網絡傳輸性能優化這項工作之前,我們需要了解浏覽器處理使用者請求的過程,那麼就必須奉上這幅神圖了:

這是navigation timing監測名額圖,從圖中我們可以看出,浏覽器在得到使用者請求之後,經曆了下面這些階段:重定向→拉取緩存→DNS查詢→建立TCP連結→發起請求→接收響應→處理HTML元素→元素加載完成。不着急,我們對其中的細節一步步展開讨論:

1.1.浏覽器緩存

我們都知道,浏覽器在向伺服器發起請求前,會先查詢本地是否有相同的檔案,如果有,就會直接拉取本地緩存,這和我們在背景部屬的Redis和Memcache類似,都是起到了中間緩沖的作用,我們先看看浏覽器處理緩存的政策:

因為網上的圖檔太籠統了,而且我翻過很多講緩存的文章,很少有将狀态碼還有什麼時候将緩存存放在記憶體(memory)中什麼時候緩存在硬碟中(disk)系統地整理出來,是以我自己繪制了一張浏覽器緩存機制流程圖,結合這張圖再更深入地說明浏覽器的緩存機制。

這裡我們可以使用chrome devtools裡的network面闆檢視網絡傳輸的相關資訊:

(這裡需要特别注意,在我們進行緩存調試時,需要去除network面闆頂部的Disable cache 勾選項,否則浏覽器将始終不會從緩存中拉取資料)

炫技,從 12.67s 到 1.06s 的網站性能優化實戰

浏覽器預設的緩存是放在記憶體内的,但我們知道,記憶體裡的緩存會因為程序的結束或者說浏覽器的關閉而被清除,而存在硬碟裡的緩存才能夠被長期保留下去。很多時候,我們在network面闆中各請求的size項裡,會看到兩種不同的狀态:from memory cache 和 from disk cache,前者指緩存來自記憶體,後者指緩存來自硬碟。而控制緩存存放位置的,不是别人,就是我們在伺服器上設定的Etag字段。在浏覽器接收到伺服器響應後,會檢測響應頭部(Header),如果有Etag字段,那麼浏覽器就會将本次緩存寫入硬碟中。

之是以拉取緩存會出現200、304兩種不同的狀态碼,取決于浏覽器是否有向伺服器發起驗證請求。隻有向伺服器發起驗證請求并确認緩存未被更新,才會傳回304狀态碼。

這裡我以nginx為例,談談如何配置緩存:

首先,我們先進入nginx的配置文檔

$ vim nginxPath/conf/nginx.conf

在配置文檔内插入如下兩項:

打開我們的網站,在chrome devtools的network面闆中觀察我們的請求資源,如果在響應頭部看見Etag和Expires字段,就說明我們的緩存配置成功了。

【!!!特别注意!!!】 在我們配置緩存時一定要切記,浏覽器在處理使用者請求時,如果命中強緩存,浏覽器會直接拉取本地緩存,不會與伺服器發生任何通信,也就是說,如果我們在伺服器端更新了檔案,并不會被浏覽器得知,就無法替換失效的緩存。是以我們在建構階段,需要為我們的靜态資源添加md5 hash字尾,避免資源更新而引起的前後端檔案無法同步的問題。

1.2.資源打包壓縮

我們之前所作的浏覽器緩存工作,隻有在使用者第二次通路我們的頁面才能起到效果,如果要在使用者首次打開頁面就實作優良的性能,必須對資源進行優化。我們常将網絡性能優化措施歸結為三大方面:減少請求數、減小請求資源體積、提升網絡傳輸速率。現在,讓我們逐個擊破:

結合前端工程化思想,我們在對上線檔案進行自動化打包編譯時,通常都需要打包工具的協助,這裡我推薦webpack,我通常都使用Gulp和Grunt來編譯node,Parcel太新,而且webpack也一直在自身的特性上向Parcel靠攏。

在對webpack進行上線配置時,我們要特别注意以下幾點:

①JS壓縮:(這點應該算是耳熟能詳了,就不多介紹了)

②HTML壓縮:

④提取css并壓縮:

在使用webpack的過程中,我們通常會以子產品的形式引入css檔案(webpack的思想不就是萬物皆子產品嘛),但是在上線的時候,我們還需要将這些css提取出來,并且壓縮,這些看似複雜的過程隻需要簡單的幾行配置就行:

(PS:我們需要用到mini-css-extract-plugin ,是以還得大家自行npm install)

如果你能按照上述六點将webpack上線配置完整配置出來,基本能将檔案資源體積壓縮到極緻了,如有疏漏,還希望大家能加以補充。

最後,我們還應該在伺服器上開啟Gzip傳輸壓縮,它能将我們的文本類檔案體積壓縮至原先的四分之一,效果立竿見影,還是切換到我們的nginx配置文檔,添加如下兩項配置項目:

【!!!特别注意!!!】 不要對圖檔檔案進行Gzip壓縮!不要對圖檔檔案進行Gzip壓縮!不要對圖檔檔案進行Gzip壓縮!我隻會告訴你效果适得其反,至于具體原因,還得考慮伺服器壓縮過程中的CPU占用還有壓縮率等名額,對圖檔進行壓縮不但會占用背景大量資源,壓縮效果其實并不可觀,可以說是“弊大于利”,是以請在gzip_types 把圖檔的相關項去掉。針對圖檔的相關處理,我們接下來會更加具體地介紹。

1.3.圖檔資源優化

剛剛我們介紹了資源打包壓縮,隻是停留在了代碼層面,而在我們實際開發中,真正占用了大量網絡傳輸資源的,并不是這些檔案,而是圖檔,如果你對圖檔進行了優化工作,你能立刻看見明顯的效果。

1.3.1.不要在HTML裡縮放圖像

很多開發者可能會有這樣的錯覺(其實我曾經也是這樣),我們會為了友善在一個200✖200的圖檔容器内直接使用一張400✖400的圖檔,我們甚至認為這樣能讓使用者覺得圖檔更加清晰,其實不然,在普通的顯示器上,使用者并不會感到縮放後的大圖更加清晰,但這一切卻導緻網頁加速速度下降,同時照成帶寬浪費,你可能不知道,一張200KB的圖檔和2M的圖檔的傳輸時間會是200m和12s的差距(親身經曆,深受其害(┬_┬))。是以,當你需要用多大的圖檔時,就在伺服器上準備好多大的圖檔,盡量固定圖檔尺寸。

1.3.2.使用雪碧圖(CSS Sprite)

雪碧圖的概念大家一定在生活中經常聽見,其實雪碧圖是減小請求數的顯著運用。而且很奇妙的是,多張圖檔聘在一塊後,總體積會比之前所有圖檔的體積之和小(你可以親自試試)。這裡給大家推薦一個自動化生成雪碧圖的工具:https://www.toptal.com/developers/css/sprite-generator (圖檔來自官網首頁)

隻要你添加相關資源檔案,他就會自動幫你生成雪碧圖以及對應的CSS樣式。

其實我們在工程中還有更為自動的方法,便是一款雪碧圖生成插件webpack-spritesmith。首先,先簡單介紹一下使用插件生成雪碧圖的思路:

首先,我們會把我們所需要的小圖示放置在一個檔案夾内以便于管理:

(這裡的@2x圖檔是為了适配視網膜二倍屏的圖檔資源,webpack-spritesmith内有專門為适配多倍屏提供的配置項,稍候将會講到)

然後,我們需要插件去讀取這個檔案夾内的所有圖檔資源檔案,以檔案夾名稱為圖檔名稱生成一張雪碧圖到指定位置,并且輸出能夠正确使用這些雪碧圖的CSS檔案。

如今,webpack-spritesmith這款插件能實作我們想要的一切,先奉上配置内容:具體可參照webpack-spritesmith官方文檔

執行webpack之後,就會在開發目錄裡生成上面兩張圖的結果,我們可以看看common.css裡面的内容:

我們可以看到,所有我們之前放在common檔案夾裡的圖檔資源都自動地生成了相應的樣式,這些都不需要我們手動處理,``webpack-spritesmith`這款插件就已經幫我們完成了!

1.3.3.使用字型圖示(iconfont)

不論是壓縮後的圖檔,還是雪碧圖,終歸還是圖檔,隻要是圖檔,就還是會占用大量網絡傳輸資源。但是字型圖示的出現,卻讓前端開發者看到了另外一個神奇的世界。

我最喜歡用的是阿裡矢量圖示庫(網址:www.iconfont.cn/),裡面有大量的矢量圖…

圖檔能做的很多事情,矢量圖都能作,而且它隻是往HTML裡插入字元和CSS樣式而已,和圖檔請求比起來資源占用完全不在一個數量級,如果你的項目裡有小圖示,就是用矢量圖吧。

但如果我們做的是公司或者團隊的項目,需要使用到許多自定義的字型圖示,可愛的設計小姐姐們隻是丢給你了幾份.svg圖檔,你又該如何去做呢?

其實也很簡單,阿裡矢量圖示庫就提供了上傳本地SVG資源的功能,這裡另外推薦一個網站——icomoon。icomoon這個網站也為我們提供了将SVG圖檔自動轉化成CSS樣式的功能。(圖檔來自icomoon首頁)

我們可以點選Import Icons按鈕導入我們本地的SVG資源,然後選中他們,接下來生成CSS的事情,就交給icomoon吧,具體的操作,就和阿裡矢量圖示庫類同了。

1.3.4.使用WebP

WebP格式,是谷歌公司開發的一種旨在加快圖檔加載速度的圖檔格式。圖檔壓縮體積大約隻有JPEG的2/3,并能節省大量的伺服器帶寬資源和資料空間。Facebook、Ebay等知名網站已經開始測試并使用WebP格式。

我們可以使用官網提供的Linux指令行工具對項目中的圖檔進行WebP編碼,也可以使用我們的線上服務,這裡我推薦叉拍雲(網址:www.upyun.com/webp)。但是在實際…

1.4.網絡傳輸性能檢測工具——Page Speed

除了network版塊,其實chrome還為我們準備好了一款監測網絡傳輸性能的插件——Page Speed,咱們的文章封面,就是用的Page Speed的官方宣傳圖(因為我覺得這張圖再合适不過了)。我們隻需要通過下面步驟安裝,就可以在chrome devtools裡找到它了:chrome菜單→更多工具→拓展程式→chrome網上應用商店→搜尋pagespeed後安轉即可。

(PS:使用chrome應用商店需要翻牆,怎麼翻牆我就不便多說了)

這就是Page Speed的功能界面:

我們隻需要打開待測試的網頁,然後點選Page Speed裡的 Start analyzing按鈕,它就會自動幫我們測試網絡傳輸性能了,這是我的網站測試結果:

Page Speed最人性化的地方,便是它會對測試網站的性能瓶頸提出完整的建議,我們可以根據它的提示進行優化工作。這裡我的網站已經優化到最好名額了(•́⌄•́๑)૭✧,Page Speed Score表示你的性能測試得分,100/100表示已經沒有需要優化的地方。

優化完畢後再使用chorme devtools的network版塊測量一下我們網頁的白屏時間還有首屏時間,是不是得到了很大的提升?

1.5.使用CDN

Last but not least,

再好的性能優化執行個體,也必須在CDN的支撐下才能到達極緻。

如果我們在Linux下使用指令$ traceroute targetIp 或者在Windows下使用批處理 > tracert targetIp,都可以定位使用者與目标計算機之間經過的所有路由器,不言而喻,使用者和伺服器之間距離越遠,經過的路由器越多,延遲也就越高。使用CDN的目的之一便是解決這一問題,當然不僅僅如此,CDN還可以分擔IDC壓力。

當然,憑着我們單個人的資金實力(除非你是王思聰)是必定搭建不起來CDN的,不過我們可以使用各大企業提供的服務,諸如騰訊雲等,配置也十分簡單,這裡就請大家自行去推敲啦。

2.頁面渲染性能優化

2.1.浏覽器渲染過程(Webkit)

其實大家應該對浏覽器将的HTML渲染機制比較熟悉了,基本流程同上圖所述,大家在入門的時候,你的導師或者前輩可能會告訴你,在渲染方面我們要減少重排和重繪,因為他們會影響浏覽器性能。不過你一定不知道其中原理是什麼,對吧。今天我們就結合《Webkit技術内幕》(這本書我還是很推薦大家買來看看,好歹作為一名前端工程師,你得知道我們天天接觸的浏覽器核心是怎樣工作的)的相關知識,給大家普及普及那些深層次的概念。

PS:這裡提到了Webkit核心,我順帶提一下浏覽器内部的渲染引擎、解釋器等元件的關系,因為經常有師弟或者一些前端愛好者向我問這方面的知識,分不清他們的關系,我就拿一張圖來說明:(如果你對着不感興趣,可以直接跳過)

炫技,從 12.67s 到 1.06s 的網站性能優化實戰

浏覽器的解釋器,是包括在渲染引擎内的,我們常說的Chrome(現在使用的是Blink引擎)和Safari使用的Webkit引擎,Firefox使用的Gecko引擎,指的就是渲染引擎。而在渲染引擎内,還包括着我們的HTML解釋器(渲染時用于構造DOM樹)、CSS解釋器(渲染時用于合成CSS規則)還有我們的JS解釋器。

2.2.DOM渲染層與GPU硬體加速

如果我告訴你,一個頁面是有許多許多層級組成的,他們就像千層面那樣,你能想象出這個頁面實際的樣子嗎?這裡為了便于大家想象,我附上一張之前Firefox的3D View插件的頁面Layers層級圖:

炫技,從 12.67s 到 1.06s 的網站性能優化實戰

對,你沒看錯,頁面的真實樣子就是這樣,是由多個DOM元素渲染層(Layers)組成的,實際上一個頁面在建構完render tree之後,是經曆了這樣的流程才最終呈現在我們面前的:

①浏覽器會先擷取DOM樹并依據樣式将其分割成多個獨立的渲染層

②CPU将每個層繪制進繪圖中

③将位圖作為紋理上傳至GPU(顯示卡)繪制

④GPU将所有的渲染層緩存(如果下次上傳的渲染層沒有發生變化,GPU就不需要對其進行重繪)并複合多個渲染層最終形成我們的圖像

從上面的步驟我們可以知道,布局是由CPU處理的,而繪制則是由GPU完成的。

其實在chrome中,也為我們提供了相關插件供我們檢視頁面渲染層的分布情況,以及GPU的占用率:(是以說,平時我們得多去嘗試嘗試chrome的那些莫名其妙的插件,真的會發現好多東西都是神器)

chrome開發者工具菜單→more tools→Layers(開啟渲染層功能子產品)

chrome開發者工具菜單→more tools→rendering(開啟渲染性能監測工具)

執行上面的操作後,你會在浏覽器裡看到這樣的效果:

太多東西了,分子產品講吧:

(一)最先是頁面右上方的小黑窗:其實提示已經說的很清楚了,它顯示的就是我們的GPU占用率,能夠讓我們清楚地知道頁面是否發生了大量的重繪。

(二)Layers版塊:這就是用于顯示我們剛提到的DOM渲染層的工具了,左側的清單裡将會列出頁面裡存在哪些渲染層,還有這些渲染層的詳細資訊。

(三)Rendering版塊:這個版塊和我們的控制台在同一個地方,大家可别找不到它。前三個勾選項是我們最常使用的,讓我來給大家解釋一下他們的功能(充當一次免費翻譯)

①Paint flashing:勾選之後會對頁面中發生重繪的元素高亮顯示

②Layer borders:和我們的Layer版塊功能類似,它會用高亮邊界突出我們頁面中的各個渲染層

③FPS meter:就是開啟我們在(一)中提到的小黑窗,用于觀察我們的GPU占用率

可能大家會問我,和我提到DOM渲染層這麼深的概念有什麼用啊,好像跟性能優化沒一點關系啊?大家應該還記得我剛說到GPU會對我們的渲染層作緩存對吧,那麼大家試想一下,如果我們把那些一直發生大量重排重繪的元素提取出來,單獨觸發一個渲染層,那樣這個元素不就不會“連累”其他元素一塊重繪了對吧。

那麼問題來了,什麼情況下會觸發渲染層呢?大家隻要記住:

video元素、WebGL、Canvas、CSS3 3D、CSS濾鏡、z-index大于某個相鄰節點的元素都會觸發新的Layer,其實我們最常用的方法,就是給某個元素加上下面的樣式:

這樣就可以觸發渲染層啦(__) 。

我們把容易觸發重排重繪的元素單獨觸發渲染層,讓它與那些“靜态”元素隔離,讓GPU分擔更多的渲染工作,我們通常把這樣的措施成為硬體加速,或者是GPU加速。大家之前肯定聽過這個說法,現在完全清楚它的原理了吧。

2.3.重排與重繪

現在到我們的重頭戲了,重排和重繪。先抛出概念:

①重排(reflow):渲染層内的元素布局發生修改,都會導緻頁面重新排列,比如視窗的尺寸發生變化、删除或添加DOM元素,修改了影響元素盒子大小的CSS屬性(諸如:width、height、padding)。

②重繪(repaint):繪制,即渲染上色,所有對元素的視覺表現屬性的修改,都會引發重繪。

我們習慣使用chrome devtools中的performance版塊來測量頁面重排重繪所占據的時間:

①藍色部分:HTML解析和網絡通信占用的時間

②黃色部分:JavaScript語句執行所占用時間

③紫色部分:重排占用時間

④綠色部分:重繪占用時間

不論是重排還是重繪,都會阻塞浏覽器。要提高網頁性能,就要降低重排和重繪的頻率和成本,盡可能少地觸發重新渲染。正如我們在2.3中提到的,重排是由CPU處理的,而重繪是由GPU處理的,CPU的處理效率遠不及GPU,并且重排一定會引發重繪,而重繪不一定會引發重排。是以在性能優化工作中,我們更應當着重減少重排的發生。

這裡給大家推薦一個網站,裡面詳細列出了哪些CSS屬性在不同的渲染引擎中是否會觸發重排或重繪:

csstriggers.com/ (圖檔來自官網)

2.4.優化政策

談了那麼多理論,最實際不過的,就是解決方案,大家一定都等着急了吧,做好準備,一大波幹貨來襲:

(一)CSS屬性讀寫分離:浏覽器每次對元素樣式進行讀操作時,都必須進行一次重新渲染(重排 + 重繪),是以我們在使用JS對元素樣式進行讀寫操作時,最好将兩者分離開,先讀後寫,避免出現兩者交叉使用的情況。最最最客觀的解決方案,就是不用JS去操作元素樣式,這也是我最推薦的。

(二)通過切換class或者style.csstext屬性去批量操作元素樣式

(三)DOM元素離線更新:當對DOM進行相關操作時,例如innerHTML、appendChild等都可以使用Document Fragment對象進行離線操作,待元素“組裝”完成後再一次插入頁面,或者使用display:none 對元素隐藏,在元素“消失”後進行相關操作。

(四)将沒用的元素設為不可見:visibility: hidden,這樣可以減小重繪的壓力,必要的時候再将元素顯示。

(五)壓縮DOM的深度,一個渲染層内不要有過深的子元素,少用DOM完成頁面樣式,多使用僞元素或者box-shadow取代。

(六)圖檔在渲染前指定大小:因為img元素是内聯元素,是以在加載圖檔後會改變寬高,嚴重的情況會導緻整個頁面重排,是以最好在渲染前就指定其大小,或者讓其脫離文檔流。

(七)對頁面中可能發生大量重排重繪的元素單獨觸發渲染層,使用GPU分擔CPU壓力。(這項政策需要慎用,得着重考量以犧牲GPU占用率能否換來可期的性能優化,畢竟頁面中存在太多的渲染層對于GPU而言也是一種不必要的壓力,通常情況下,我們會對動畫元素采取硬體加速。)

3.JS阻塞性能

JavaScript在網站開發中幾乎已經确定了壟斷地位,哪怕是一個再簡單不過的靜态頁面,你都可能看到JS的存在,可以說,沒有JS,就基本沒有使用者互動。然而,腳本帶來的問題就是他會阻塞頁面的平行下載下傳,還會提高程序的CPU占用率。更有甚者,現在node.js已經在前端開發中普及,稍有不慎,我們引發了記憶體洩漏,或者在代碼中誤寫了死循環,會直接造成我們的伺服器奔潰。在如今這個JS已經遍布前後端的時代,性能的瓶頸不單單隻是停留在影響使用者體驗上,還會有更多更為嚴重的問題,對JS的性能優化工作不可小觑。

在程式設計的過程中,如果我們使用了閉包後未将相關資源加以釋放,或者引用了外鍊後未将其置空(比如給某DOM元素綁定了事件回調,後來卻remove了該元素),都會造成記憶體洩漏的情況發生,進而大量占用使用者的CPU,造成卡頓或當機。我們可以使用chrome提供的JavaScript Profile版塊,開啟方式同Layers等版塊,這裡我就不再多說了,直接上效果圖:

我們可以清除看見JS執行時各函數的執行時間以及CPU占用情況,如果我在代碼裡增加一行while(true){}, 那麼它的占用率一定會飙升到一個異常的名額(親測93.26%)。

其實浏覽器強大的記憶體回收機制在大多數時候避免了這一情況的發生,即便使用者發生了當機,他隻要結束相關程序(或關閉浏覽器)就可以解決這一問題,但我們要知道,同樣的情況還會發生在我們的伺服器端,也就是我們的node中,嚴重的情況,會直接造成我們的伺服器當機,網站奔潰。是以更多時候,我們都使用JavaScript Profile版塊來進行我們的node服務的壓力測試,搭配node-inspector 插件,我們能更有效地檢測JS執行時各函數的CPU占用率,針對性地進行優化。

(PS:沒修煉到一定水準,千萬别在服務端使用閉包,一個是真沒啥用,我們會有更多優良的解決辦法,二是真的很容易記憶體洩漏,造成的後果是你無法預期的)

4.【拓展】負載均衡

之是以将負載均衡作為拓展内容,是因為如果是你自己搭建的個人網站,或者中小型網站,其實并不需要考慮多大的并發量,但是如果你搭建的是大型網站,負載均衡便是開發過程不可或缺的步驟。

4.1.Node.js處理IO密集型請求

現在的開發流程都注重前後端分離,也就是軟體工程中常提到的“高内聚低耦合”的思想,你也可以用子產品化的思想去了解,前後解耦就相當于把一個項目分成了前端和後端兩個大子產品,中間通過接口聯系起來,分别進行開發。這樣做有什麼好處?我就舉最有實際效果的一點:“異步程式設計”。這是我自己想的名字,因為我覺得前後解耦的形式很像我們JS中的異步隊列,傳統的開發模式是“同步”的,前端需要等後端封裝好接口,知道了能拿什麼資料,再去開發,時間短,工程大。而解耦之後,我們隻需要提前約定好接口,前後兩端就可以同時開發,不僅高效而且省時。

我們都知道node的核心是事件驅動,通過loop去異步處理使用者請求,相比于傳統的後端服務,它們都是将使用者的每個請求配置設定到異步隊列進行處理,推薦大家去看這樣一篇博文:Node.js : 我隻需要一個店小二。特别生動地講解了事件驅動的運作機制,通俗易懂。事件驅動的最大優勢是什麼?就是在高并發IO時,不會造成堵塞,對于直播類網站,這點是至關重要的,我們有成功的先例——快手,快手強大的IO高并發究其本質一定能追溯到node。

其實作在的企業級網站,都會搭建一層node作為中間層。大概的網站架構如圖所示:

4.2.pm2實作Node.js“多線程”

我們都知道node的優劣,這裡分享一份連結,找了挺久寫的還算詳細:https://www.zhihu.com/question/19653241/answer/15993549。其實都是老套路,那些說node不行的都是指着node是單線程這一個軟肋開撕,告訴你,我們有解決方案了——pm2。這是它的官網:pm2.keymetrics.io/ 。它是一款node.js程序管理器,具體的功能,就是能在你的計算機裡的每一個核心都啟動一個node.js服務,也就是說如果你的電腦或者伺服器是多核處理器(現在也少見單核了吧),它就能啟動多個node.js服務,并且它能夠自動控制負載均衡,會自動将使用者的請求分發至壓力小的服務程序上處理。聽起來這東西簡直就是神器啊!而且它的功能遠遠不止這些,這裡我就不作過多介紹了,大家知道我們在上線的時候需要用到它就行了,安裝的方法也很簡單,直接用npm下到全局就可以了$ npm i pm2 -g具體的使用方法還有相關特性可以參照官網。這裡我在build檔案夾内添加了pm2.json檔案,這是pm2的啟動配置檔案,我們可以自行配置相關參數,具體可參考github源碼,運作時我們隻要在上線目錄下輸入指令$ pm2 start pm2.json即可。

下面是pm2啟動後的效果圖:

4.3.nginx搭建反向代理

在開始搭建工作之前,首先得知道什麼是反向代理。可能大家對這個名詞比較陌生,先上一張圖:

所謂代理就是我們通常所說的中介,網站的反向代理就是指那台介于使用者和我們真實伺服器之間的伺服器(說的我都拗口了),它的作用便是能夠将使用者的請求配置設定到壓力較小的伺服器上,其機制是輪詢。聽完這句話是不是感覺很耳熟,沒錯,在我介紹pm2的時候也說過同樣的話,反向代理起到的作用同pm2一樣也是實作負載均衡,你現在應該也明白了兩者之間的差異,反向代理是對伺服器實作負載均衡,而pm2是對程序實作負載均衡。大家如果想深入了解反向代理的相關知識,我推薦知乎的一個貼子:www.zhihu.com/question/24… 。但是大家會想到,配伺服器是運維的事情啊,和我們前端有什麼關系呢?的确,在這部分,我們的工作隻有一些,隻需要向運維提供一份配置文檔即可。

也就是說,在和運維對接的時候,我們隻需要将上面這幾行代碼改為我們配置好的文檔發送給他就行了,其他的事情,運維小哥會明白的,不用多說,都在酒裡。

但是,這幾行代碼該怎麼去改呢?首先我們得知道,在nginx中,子產品被分為三大類:handler、filter和upstream。而其中的upstream子產品,負責完成完成網絡資料的接收、處理和轉發,也是我們需要在反向代理中用到的子產品。接下來我們将介紹配置代碼裡的内容所表示的含義

4.3.1.upstream配置資訊:

upstream關鍵字後緊跟的辨別符是我們自定義的項目名稱,通過一對花括号在其中增添我們的配置資訊。

ip_hash 關鍵字:控制使用者再次通路時是否連接配接到前一次連接配接的伺服器

server關鍵字:我們真實伺服器的位址,這裡的内容肯定是需要我們去填寫的,不然運維怎麼知道你把項目放在那個伺服器上了,也不知道你封裝了一層node而得去監聽3000端口。

4.3.2.server配置資訊

server是nginx的基本配置,我們需要通過server将我們定義的upstream應用到伺服器上。

listen關鍵字:伺服器監聽的端口

location關鍵字:和我們之前在node層說到的路由是起同樣的功能,這裡是把使用者的請求配置設定到對應的upstream上

繼續閱讀