優化網站的存取速度

基礎知識

很多站長都遇到了網站存取速度不夠快的問題,今天來嘗試了解並解決一下這個問題。先來說一下我們使用瀏覽器存取一個網頁,到看到這個網頁出現,中間到底經過了些什麼?

首先,在 HTTP 請求之前需要做這些:

  • 獲取 IP。在瀏覽器網址列中輸入網址並提交之後,首先它會在 DNS 本機快取表中尋找,如果有則直接告知 IP 位址。如果沒有則要求閘道器 DNS 進行尋找,如此下去,當找到對應的 IP 後,返回給瀏覽器。
  • 建立 TCP 連線。當獲取到 IP 之後,就開始與所請求的伺服器完成三次握手建立 TCP 連線。
  • 連線建立後,就向伺服器發出 HTTP 請求。

HTTP 請求,首先會得到頁面檔案,然後解析頁面檔案中的資源檔案,包括 CSS、JS、圖片等,再發請求獲取這些資源檔案。在 HTTP 1.1 請求中,多個請求是可以重疊進行的,但是頁面檔案必須要先到達才能知道要去請求哪些資源檔案。

所以整個過程中有幾個階段,第一階段是首字節獲取時間,也就是從 URL 請求到伺服器收到 HTTP 請求後返回回應內容的時間,這其實並不只是 DNS 和建立連線的時間,對於動態頁面來說,要由伺服器將動態程式碼執行完畢返回頁面程式碼才可以,於是包括運算和資料庫操作啊這些都會直接增加首字節獲取時間。而對於靜態檔案來說,首字節獲取時間通常是比較快的。但是如果與伺服器之間的網絡不暢,比如說伺服器在國外,則會造成很長的延時。

在阿里雲的雲監控中,可以任意設定 HTTP 監控點,來監控伺服器的回應時間,比如說我設定了放在同一個伺服器上的兩個網站:

HTTP监控

這裡可以明顯看到對於需要調用資料庫的 WordPress 來說,回應時間明顯比下面只使用了簡單的 XML 讀取的 eitdesign 要長得多。而對於 eitdesign 來講,基本上回應時間是瞬間,這裡就看出伺服器物理距離帶來的影響了,因為伺服器在杭州,從杭州存取只要 2ms,而從青島存取則需要 23ms。至於數據到伺服器再返回的回應時間可以透過 ping 指令獲取。

話說很多人都用過的 ping 指令,但是貌似很多人只是用來查看伺服器通不通。。。先來說說 ping 的原理:發送一個 ICMP 回聲請求訊息給目的地並報告是否收到所希望的 ICMP 回聲應答。
理論上來說,ping 發過去的數據大小對方也應該回覆同樣大小的數據,於是可以輕鬆地看出與伺服器之間通訊的狀態。

所以如果 ping 的回應時間一直很穩定,突然有波動,可能是由於頻寬突然被佔滿導致的,這時配合對於伺服器資源的監控,可以輕鬆地看出問題出在哪。

PING

在 ping 的同時不斷重新整理頁面,可以看到某個瞬間明顯時間變長的現象,就是這一瞬間頻寬已經被佔滿了。

第二階段是得到頁面檔案的時間,在頁面檔案得到之前,是不會請求任何資源檔案的,因為還不知道頁面上有哪些資源檔案,所以這段時間也非常關鍵。

第三階段是獲取 head 中各種資源檔案的時間,資源檔案是以在 HTML 頁面中出現的順序來載入的,所以 head 中的資源會優先被載入,head 裡主要是 CSS 和 JS 檔案,然後頁面才會渲染出來,所以要特別注意 head 中所需資源的載入時間。畢竟在頁面渲染出來之前,使用者所看到的都是一片空白。

第四階段是獲取剩下的資源檔案的時間,這部分主要是圖片動畫影片等檔案了,重要性不是那麼高了,畢竟頁面已經出來了,大部分使用者覺得用幾秒時間看著它們載入也是可以接受的。

其實還有頁面渲染時間,但是因為和載入同步進行,而且通常不會比載入更慢,所以可以忽略。

測試工具

想要直觀地獲取這些數據,可以直接看頁面的時間線,也就是瀑布圖,現在各大瀏覽器內建的除錯工具都可以實現這個功能。以本網站和 Safari 為例,可以看下圖:
首次存取(Safari 中可以透過 Shift + 重新整理按鈕來忽略快取):

瀑布图

再次存取:

再次访问

從圖中可以清楚地看到,共引用了 47 個資源檔案,一共是 2M 的數據,首次存取總用時 1.07 秒,再次存取用時 825ms。先來說說首次存取:
第一行藍色的這個是頁面檔案,總大小 50.89KB,壓縮過實際傳輸尺寸 10.58KB,回應時間 342ms,載入時間 66.1ms。然後這個檔案解析之後,就開始請求各個資源檔案。

這裡可以看到兩條虛線,藍色的這一條是 DOMContent 事件觸發時間,此處是 635ms,表示當瀏覽器已經完成解析文件(但其他資源比如圖片可能還沒下載完成),而紅色的這條是 Load 事件觸發時間,這裡是 1.07 秒,表示所有資源都已經載入完成了。

首次存取時所有資源都要請求,而再次存取時就可以利用本機快取數據加快資源載入速度了。所以對不常變化的靜態資源設定一個過期時間,告訴瀏覽器在一定期間內都不需要重新載入這個資源。可以加快使用者再次存取的速度,顯著減少第四階段的時間。

這個時間線不僅可以在本機瀏覽器中檢視,也可以在相關的測試網站上看到,例如阿里的阿里測, 還有Google Page SpeedYahoo YSlow。有一個工具把 Google Page Speed 和 Yahoo YSlow 結合了一下,叫GTMetrix,也是不錯的工具。同時這些網站也會針對站點提供優化的建議。

還有個工具叫17CE,可以同時從不同地區的測試伺服器測試回應時間和 PING 時間,可以用於了解不同地區使用者的存取速度。

優化方法

好了,現在有了相關的基礎知識和工具,就可以有針對性地進行優化了。下面來分別說一說各部分要如何來優化:

第一階段,伺服器回應時間,這部分基本上沒什麼太好的方法了,如果是動態網站的話,主要以演算法和資料庫優化為主,還有使用 AJAX 非同步讀取數據之類的,其實是後端的事,這裡就不展開討論了。不過一個網站如果伺服器回應時間超過 2 秒,基本上可以認為這伺服器已經掛了,通常應該控制在 500ms 以內,或許讓人感覺並不明顯,如果能控制在 250ms 以內就更好了。

第二階段,獲取頁面檔案,首先頁面檔案通常都不大,而且都是純文字。於是優化的方法就是開啟 Gzip 壓縮,開啟方式,對於 Apache 來說,首先要把 httpd.conf 裡的 LoadModule deflate_module modules/mod_deflate.so 前的 # 去掉,然後重新啟動 Apache,然後在 .htaccess 中加入:

<IfModule mod_deflate.c>
	AddOutputFilter DEFLATE html xml php js css text/html text/plain
</IfModule>

Gzip 壓縮對於這種比較鬆散的純文字效果還是比較明顯的,比如說我的這個首頁就從 50K 壓縮到了 10K。

還有要使用外部 link CSS 檔案,不要把樣式表直接放入 HTML 頁在中,這樣 CSS 檔案可以設定快取。

第三階段,head 中的資源檔案,主要是 CSS 和 JS 檔案,方法有這幾個:

  • 使用 GZip 壓縮。
  • 使用 minify 之後的 JS 和 CSS,原版用於修改,輸出 min 版用於使用,雖然不利於閱讀,但是尺寸明顯減小。
  • 合併多個 CSS 和 JS 檔案,減少 HTTP 請求數量。
  • 把不必要的 JS 檔案移到頁面後面去載入,對於那些不影響渲染的 JS 檔案,移到第四階段再載入可以減少頁面顯示時間。
  • 對於不常更新的檔案設定快取時間並使用 OSS 或 CDN

第四階段,這一段才是真正的大數據量,現在通常使用者的頻寬都不是問題了,瓶頸主要出現在伺服器上了,可以想想看,如果一個頁面完全載入需要 2MB 的數據,那麼如果伺服器出口頻寬只有 1Mbps 的話,則忽略各種延時不計,在只有一個使用者存取的情況下,最快也需要 16 秒的時間才能傳輸完成這 2MB 的數據。這對於使用者來說是不能忍的。

於是對於阿里雲的 ECS 來說,如果你是包月的,沒有很高頻寬的話,就要盡量減少一切從 ECS 上直接存取的資源。方法主要是使用 OSS 儲存,CDN 加速和 GZip 壓縮。這具體的優化就很細緻了,努力將 ECS 上直接存取的數據量減至最少,但是 WordPress 很麻煩,有些系統自帶的和外掛程式裡引用的 JS 和 CSS 檔案,不方便合併和改變位置。。。只能盡量優化。以我這個網站為例,之前在將所有圖片都放入 CDN 之後,載入首頁還是大概要有 220KB 的數據要從 ECS 上走,這樣只有 1Mbps 頻寬的話,至少需要 2 秒時間。後來又移動了主題內的所有圖片和 Bootstrap、jQuery 至 CDN,再加上 GZip 壓縮,經過優化之後,現在只有 80KB 的數據,可以保證在 1 秒以內載入完成。

其它需要注意的地方包括:

  • 在保證圖片質量的前提下盡可能的壓縮圖片尺寸。
  • 如果要展示小圖,不要在頁面中 resize 大圖,要直接使用小尺寸的圖片。
  • 盡量針對靜態檔案單獨使用無 Cookies 的域名

至於存取量巨大的網站,那更是節約每一個字節都很重要,還有更多的優化方法,可以具體參考 Google Page Speed 和 Yahoo YSlow 的頁面評測結果,不過這兩個伺服器都在國外,所以回應時間會長很多,這一數據可以忽略。

最後來說說如何設定快取時間,對於 Apache 來說,首先要把 httpd.conf 裡的 LoadModule expires_module modules/mod_expires.so 前的 # 去掉,然後重新啟動 Apache,然後在 .htaccess 中加入相應的程式碼就可以設定不同檔案類型的快取時間了,如下設定的是圖片檔案和 JS 檔案 1 個月,圖示檔案 1 年:

<IfModule mod_expires.c>
	ExpiresActive On
	ExpiresByType image/jpg "access plus 1 month"
	ExpiresByType image/jpeg "access plus 1 month"
	ExpiresByType image/gif "access plus 1 month"
	ExpiresByType image/png "access plus 1 month"
	ExpiresByType text/x-javascript "access plus 1 month"
	ExpiresByType application/x-shockwave-flash "access plus 1 month"
	ExpiresByType image/x-icon "access plus 1 year"
</IfModule>

題外話

其實這篇文章的誕生源於阿里雲伺服器的小故障,某一天開始回應時間變的非常長,以至於雲監控警報,當時上去看了一下—度回應時間達到 15 秒以上。。。

於是在想是發生什麼了。。。就想盡各種辦法開始優化自己的網站,雖然這故障很快修復了,但是有機會讓我重新研究了一下頁面加速的各種知識。讓我清醒地意識到 1Mbps 頻寬是多麼多麼的小。。。

希望大家在這篇文章中能找到自己想要的東西,小小的加速一下自己的網站~~~

評論請移步微信公眾號

, ,


讚賞