1. 程式人生 > >漫談 HTTP 效能優化

漫談 HTTP 效能優化

本文主要是側重於 HTTP 的優化,對於 HTTPS 後續文章會講。

既然要做效能優化,那麼,我們就需要知道:什麼是效能?它都有哪些指標,又應該如何度量,進而採取哪些手段去優化?

“效能”其實是一個複雜的概念。不同的人、不同的應用場景都會對它有不同的定義。對於 HTTP 來說,它又是一個非常複雜的系統,裡面有非常多的角色,所以很難用一兩個簡單的詞就能把效能描述清楚。

還是從 HTTP 最基本的“請求 - 應答”模型來著手吧。在這個模型裡有兩個角色:客戶端和伺服器,還有中間的傳輸鏈路,考查效能就可以看這三個部分。

HTTP 伺服器

我們先來看看伺服器,它一般執行在 Linux 作業系統上,用 Apache、Nginx 等 Web 伺服器軟體對外提供服務,所以,效能的含義就是它的服務能力,也就是儘可能多、儘可能快地處理使用者的請求。

衡量伺服器效能的主要指標有三個:吞吐量(requests per second)、併發數(concurrency)和響應時間(time per request)。

吞吐量就是我們常說的 RPS,每秒的請求次數,也有叫 TPS、QPS,它是伺服器最基本的效能指標,RPS 越高就說明伺服器的效能越好。

併發數反映的是伺服器的負載能力,也就是伺服器能夠同時支援的客戶端數量,當然也是越多越好,能夠服務更多的使用者。

響應時間反映的是伺服器的處理能力,也就是快慢程度,響應時間越短,單位時間內伺服器就能夠給越多的使用者提供服務,提高吞吐量和併發數。

除了上面的三個基本效能指標,伺服器還要考慮 CPU、記憶體、硬碟和網絡卡等系統資源的佔用程度,利用率過高或者過低都可能有問題。

在 HTTP 多年的發展過程中,已經出現了很多成熟的工具來測量這些伺服器的效能指標,開源的、商業的、命令列的、圖形化的都有。

在 Linux 上,最常用的效能測試工具可能就是 ab(Apache Bench)了,比如,下面的命令指定了併發數 100,總共傳送 10000 個請求:

ab -c 100 -n 10000 'http://www.xxx.com'
系統資源監控方面,Linux 自帶的工具也非常多,常用的有 uptime、top、vmstat、netstat、sar 等等,可能你比我還要熟悉,我就列幾個簡單的例子吧:複製程式碼
top // 檢視 CPU 和記憶體佔用情況
vmstat 2 // 每 2 秒檢查一次系統狀態
sar -n DEV 2 // 看所有網絡卡的流量,定時 2 秒檢查

理解了這些效能指標,我們就知道了伺服器的效能優化方向:合理利用系統資源,提高伺服器的吞吐量和併發數,降低響應時間。

HTTP 客戶端

看完了伺服器的效能指標,我們再來看看如何度量客戶端的效能。

客戶端是資訊的消費者,一切資料都要通過網路從伺服器獲取,所以它最基本的效能指標就是“延遲”(latency)。

之前在講 HTTP/2 的時候就簡單介紹過延遲。所謂的“延遲”其實就是“等待”,等待資料到達客戶端時所花費的時間。但因為 HTTP 的傳輸鏈路很複雜,所以延遲的原因也就多種多樣。

  • 首先,我們必須謹記有一個“不可逾越”的障礙——光速,因為地理距離而導致的延遲是無法克服的,訪問數千公里外的網站顯然會有更大的延遲。

  • 其次,第二個因素是頻寬,它又包括接入網際網路時的電纜、WiFi、4G 和運營商內部網路、運營商之間網路的各種頻寬,每一處都有可能成為資料傳輸的瓶頸,降低傳輸速度,增加延遲。

  • 第三個因素是 DNS 查詢,如果域名在本地沒有快取,就必須向 DNS 系統發起查詢,引發一連串的網路通訊成本,而在獲取 IP 地址之前客戶端只能等待,無法訪問網站,

  • 第四個因素是 TCP 握手,你應該對它比較熟悉了吧,必須要經過 SYN、SYN/ACK、ACK 三個包之後才能建立連線,它帶來的延遲由光速和頻寬共同決定。

建立 TCP 連線之後,就是正常的資料收發了,後面還有解析 HTML、執行 JavaScript、排版渲染等等,這些也會耗費一些時間。不過它們已經不屬於 HTTP 了,所以不在今天的討論範圍之內。

之前講 HTTPS 時介紹過一個專門的網站“SSLLabs”,而對於 HTTP 效能優化,也有一個專門的測試網站“WebPageTest”。它的特點是在世界各地建立了很多的測試點,可以任意選擇地理位置、機型、作業系統和瀏覽器發起測試,非常方便,用法也很簡單。

網站測試的最終結果是一個直觀的“瀑布圖”(Waterfall Chart),清晰地列出了頁面中所有資源載入的先後順序和時間消耗,比如下圖就是對 GitHub 首頁的一次測試。

Chrome 等瀏覽器自帶的開發者工具也可以很好地觀察客戶端延遲指標,面板左邊有每個 URI 具體消耗的時間,面板的右邊也是類似的瀑布圖。

點選某個 URI,在 Timing 頁裡會顯示出一個小型的“瀑布圖”,是這個資源消耗時間的詳細分解,延遲的原因都列的清清楚楚,比如下面的這張圖:

圖裡面的這些指標都是什麼含義呢?我給你解釋一下:

  • 因為有“隊頭阻塞”,瀏覽器對每個域名最多開 6 個併發連線(HTTP/1.1),當頁面裡連結很多的時候就必須排隊等待(Queued、Queueing),這裡它就等待了 1.62 秒,然後才被瀏覽器正式處理;

  • 瀏覽器要預先分配資源,排程連線,花費了 11.56 毫秒(Stalled);

  • 連線前必須要解析域名,這裡因為有本地快取,所以只消耗了 0.41 毫秒(DNS Lookup);

  • 與網站伺服器建立連線的成本很高,總共花費了 270.87 毫秒,其中有 134.89 毫秒用於 TLS 握手,那麼 TCP 握手的時間就是 135.98 毫秒(Initial connection、SSL);

  • 實際傳送資料非常快,只用了 0.11 毫秒(Request sent);

  • 之後就是等待伺服器的響應,專有名詞叫 TTFB(Time To First Byte),也就是“首位元組響應時間”,裡面包括了伺服器的處理時間和網路傳輸時間,花了 124.2 毫秒;

  • 接收資料也是非常快的,用了 3.58 毫秒(Content Dowload)。

從這張圖你可以看到,一次 HTTP“請求 - 響應”的過程中延遲的時間是非常驚人的,總時間 415.04 毫秒裡佔了差不多 99%。

所以,客戶端 HTTP 效能優化的關鍵就是:降低延遲。

HTTP 傳輸鏈路

以 HTTP 基本的“請求 - 應答”模型為出發點,剛才我們得到了 HTTP 效能優化的一些指標,現在,我們來把視角放大到“真實的世界”,看看客戶端和伺服器之間的傳輸鏈路,它也是影響 HTTP 效能的關鍵。

這就是所謂的“第一公里”“中間一公里”和“最後一公里”(在英語原文中是 mile,英里)。

“第一公里”是指網站的出口,也就是伺服器接入網際網路的傳輸線路,它的頻寬直接決定了網站對外的服務能力,也就是吞吐量等指標。顯然,優化效能應該在這“第一公里”加大投入,儘量購買大頻寬,接入更多的運營商網路。

“中間一公里”就是由許多小網路組成的實際的網際網路,其實它遠不止“一公里”,而是非常非常龐大和複雜的網路,地理距離、網路互通都嚴重影響了傳輸速度。好在這裡面有一個 HTTP 的“好幫手”——CDN,它可以幫助網站跨越“千山萬水”,讓這段距離看起來真的就好像只有“一公里”。

“最後一公里”是使用者訪問網際網路的入口,對於固網使用者就是光纖、網線,對於移動使用者就是 WiFi、基站。以前它是客戶端效能的主要瓶頸,延遲大頻寬小,但隨著近幾年 4G 和高速寬頻的普及,“最後一公里”的情況已經好了很多,不再是制約效能的主要因素了。

除了這“三公里”,我個人認為還有一個“第零公里”, 就是網站內部的 Web 服務系統。它其實也是一個小型的網路(當然也可能會非常大),中間的資料處理、傳輸會導致延遲,增加伺服器的響應時間,也是一個不可忽視的優化點。

在上面整個網際網路傳輸鏈路中,末端的“最後一公里”我們是無法控制的,所以我們只能在“第零公里”“第一公里”和“中間一公里”這幾個部分下功夫,增加頻寬降低延遲,優化傳輸速度。

但因為我們是無法完全控制客戶端的,所以實際上的優化工作通常是在伺服器端。這裡又可以細分為後端和前端,後端是指網站的後臺服務,而前端就是 HTML、CSS、圖片等展現在客戶端的程式碼和資料。

知道了大致的方向,HTTP 效能優化具體應該怎麼做呢?

總的來說,任何計算機系統的優化都可以分成這麼幾類:硬體軟體、內部外部、花錢不花錢。

投資購買現成的硬體最簡單的優化方式,比如換上更強的 CPU、更快的網絡卡、更大的頻寬、更多的伺服器,效果也會“立竿見影”,直接提升網站的服務能力,也就實現了 HTTP 優化。

另外,花錢購買外部的軟體或者服務也是一種行之有效的優化方式,最“物有所值”的應該算是 CDN 了。CDN 專注於網路內容交付,幫助網站解決“中間一公里”的問題,還有很多其他非常專業的優化功能。把網站交給 CDN 運營,就好像是“讓網站坐上了噴氣飛機”,能夠直達使用者,幾乎不需要費什麼力氣就能夠達成很好的優化效果。

不過這些“花錢”的手段實在是太沒有“技術含量”了,屬於“懶人”(無貶義)的做法,所以我就不再細說,接下來重點就講講在網站內部、“不花錢”的軟體優化。

我把這方面的 HTTP 效能優化概括為三個關鍵詞:開源、節流、快取。

開源

這個“開源”可不是 Open Source,而是指抓“源頭”,開發網站伺服器自身的潛力,在現有條件不變的情況下儘量挖掘出更多的服務能力。

首先,我們應該選用高效能的 Web 伺服器,最佳選擇當然就是 Nginx/OpenResty 了,儘量不要選擇基於 Java、Python、Ruby 的其他伺服器,它們用來做後面的業務邏輯伺服器更好。利用 Nginx 強大的反向代理能力實現“動靜分離”,動態頁面交給 Tomcat、Django、Rails,圖片、樣式表等靜態資源交給 Nginx。

Nginx 或者 OpenResty 自身也有很多配置引數可以用來進一步調優,舉幾個例子,比如說禁用負載均衡鎖、增大連線池,繫結 CPU 等等,相關的資料有很多。

特別要說的是,對於 HTTP 協議一定要啟用長連線。TCP 和 SSL 建立新連線的成本是非常高的,有可能會佔到客戶端總延遲的一半以上。長連線雖然不能優化連線握手,但可以把成本“均攤”到多次請求裡,這樣只有第一次請求會有延遲,之後的請求就不會有連線延遲,總體的延遲也就降低了。

另外,在現代作業系統上都已經支援 TCP 的新特性“TCP Fast Open”(Win10、iOS9、Linux 4.1),它的效果類似 TLS 的“False Start”,可以在初次握手的時候就傳輸資料,也就是 0-RTT,所以我們應該儘可能在作業系統和 Nginx 裡開啟這個特性,減少外網和內網裡的握手延遲。

下面給出一個簡短的 Nginx 配置示例,啟用了長連線等優化引數,實現了動靜分離:

server {
  listen 80 deferred reuseport backlog=4096 fastopen=1024; 
 
 
  keepalive_timeout  60;
  keepalive_requests 10000;
  
  location ~* \.(png)$ {
    root /var/images/png/;
  }
  
  location ~* \.(php)$ {
    proxy_pass http://php_back_end;
  }
}

節流

“節流”是指減少客戶端和伺服器之間收發的資料量,在有限的頻寬裡傳輸更多的內容。

“節流”最基本的做法就是使用 HTTP 協議內建的“資料壓縮”編碼,不僅可以選擇標準的 gzip,還可以積極嘗試新的壓縮演算法 br,它有更好的壓縮效果。

不過在資料壓縮的時候應當注意選擇適當的壓縮率,不要追求最高壓縮比,否則會耗費伺服器的計算資源,增加響應時間,降低服務能力,反而會“得不償失”。

gzip 和 br 是通用的壓縮演算法,對於 HTTP 協議傳輸的各種格式資料,我們還可以有針對性地採用特殊的壓縮方式。

HTML/CSS/JS 屬於純文字,就可以採用特殊的“壓縮”,去掉原始碼裡多餘的空格、換行、註釋等元素。這樣“壓縮”之後的文字雖然看起來很混亂,對“人類”不友好,但計算機仍然能夠毫無障礙地閱讀,不影響瀏覽器上的執行效果。

圖片在 HTTP 傳輸裡佔有非常高的比例,雖然它本身已經被壓縮過了,不能被 gzip、br 處理,但仍然有優化的空間。比如說,去除圖片裡的拍攝時間、地點、機型等元資料,適當降低解析度,縮小尺寸。圖片的格式也很關鍵,儘量選擇高壓縮率的格式,有損格式應該用 JPEG,無損格式應該用 Webp 格式。

對於小文字或者小圖片,還有一種叫做“資源合併”(Concatenation)的優化方式,就是把許多小資源合併成一個大資源,用一個請求全下載到客戶端,然後客戶端再用 JS、CSS 切分後使用,好處是節省了請求次數,但缺點是處理比較麻煩。

剛才說的幾種資料壓縮都是針對的 HTTP 報文裡的 body,在 HTTP/1 裡沒有辦法可以壓縮 header,但我們也可以採取一些手段來減少 header 的大小,不必要的欄位就儘量不發(例如 Server、X-Powered-By)。

網站經常會使用 Cookie 來記錄使用者的資料,瀏覽器訪問網站時每次都會帶上 Cookie,冗餘度很高。所以應當少使用 Cookie,減少 Cookie 記錄的資料量,總使用 domain 和 path 屬性限定 Cookie 的作用域,儘可能減少 Cookie 的傳輸。如果客戶端是現代瀏覽器,還可以使用 HTML5 裡定義的 Web Local Storage,避免使用 Cookie。

壓縮之外,“節流”還有兩個優化點,就是域名和重定向。

DNS 解析域名會耗費不少的時間,如果網站擁有多個域名,那麼域名解析獲取 IP 地址就是一個不小的成本,所以應當適當“收縮”域名,限制在兩三個左右,減少解析完整域名所需的時間,讓客戶端儘快從系統快取裡獲取解析結果。

重定向引發的客戶端延遲也很高,它不僅增加了一次請求往返,還有可能導致新域名的 DNS 解析,是 HTTP 前端效能優化的“大忌”。除非必要,應當儘量不使用重定向,或者使用 Web 伺服器的“內部重定向”。

快取

“快取”不僅是 HTTP,也是任何計算機系統性能優化的“法寶”,把它和上面的“開源”“節流”搭配起來應用於傳輸鏈路,就能夠讓 HTTP 的效能再上一個臺階。

在“第零公里”,也就是網站系統內部,可以使用 Memcache、Redis、Varnish 等專門的快取服務,把計算的中間結果和資源儲存在記憶體或者硬盤裡,Web 伺服器首先檢查快取系統,如果有資料就立即返回給客戶端,省去了訪問後臺服務的時間。

在“中間一公里”,快取更是效能優化的重要手段,CDN 的網路加速功能就是建立在快取的基礎之上的,可以這麼說,如果沒有快取,那就沒有 CDN。

利用好快取功能的關鍵是理解它的工作原理,為每個資源都新增 ETag 和 Last-modified 欄位,再用 Cache-Control、Expires 設定好快取控制屬性。

其中最基本的是 max-age 有效期,標記資源可快取的時間。對於圖片、CSS 等靜態資源可以設定較長的時間,比如一天或者一個月,對於動態資源,除非是實時性非常高,也可以設定一個較短的時間,比如 1 秒或者 5 秒。

這樣一旦資源到達客戶端,就會被快取起來,在有效期內都不會再向伺服器傳送請求,也就是:“沒有請求的請求,才是最快的請求。”

HTTP/2

在“開源”“節流”和“快取”這三大策略之外,HTTP 效能優化還有一個選擇,那就是把協議由 HTTP/1 升級到 HTTP/2。

通過“飛翔篇”的學習,你已經知道了 HTTP/2 的很多優點,它消除了應用層的隊頭阻塞,擁有頭部壓縮、二進位制幀、多路複用、流量控制、伺服器推送等許多新特性,大幅度提升了 HTTP 的傳輸效率。

實際上這些特性也是在“開源”和“節流”這兩點上做文章,但因為這些都已經內建在了協議內,所以只要換上 HTTP/2,網站就能夠立刻獲得顯著的效能提升。

不過你要注意,一些在 HTTP/1 裡的優化手段到了 HTTP/2 裡會有“反效果”。

對於 HTTP/2 來說,一個域名使用一個 TCP 連線才能夠獲得最佳效能,如果開多個域名,就會浪費頻寬和伺服器資源,也會降低 HTTP/2 的效率,所以“域名收縮”在 HTTP/2 裡是必須要做的。

“資源合併”在 HTTP/1 裡減少了多次請求的成本,但在 HTTP/2 裡因為有頭部壓縮和多路複用,傳輸小檔案的成本很低,所以合併就失去了意義。而且“資源合併”還有一個缺點,就是降低了快取的可用性,只要一個小檔案更新,整個快取就完全失效,必須重新下載。

所以在現在的大頻寬和 CDN 應用場景下,應當儘量少用資源合併(JS、CSS 圖片合併,資料內嵌),讓資源的粒度儘可能地小,才能更好地發揮快取的作用。

小結

  1. 效能優化是一個複雜的概念,在 HTTP 裡可以分解為伺服器效能優化、客戶端效能優化和傳輸鏈路優化;

  2. 伺服器有三個主要的效能指標:吞吐量、併發數和響應時間,此外還需要考慮資源利用率;

  3. 客戶端的基本效能指標是延遲,影響因素有地理距離、頻寬、DNS 查詢、TCP 握手等;

  4. 從伺服器到客戶端的傳輸鏈路可以分為三個部分,我們能夠優化的是前兩個部分,也就是“第一公里”和“中間一公里”;

  5. 有很多工具可以測量這些指標,伺服器端有 ab、top、sar 等,客戶端可以使用測試網站,瀏覽器的開發者工具。

  6. 花錢購買硬體、軟體或者服務可以直接提升網站的服務能力,其中最有價值的是 CDN;

  7. 不花錢也可以優化 HTTP,三個關鍵詞是“開源”“節流”和“快取”;

  8. 後端應該選用高效能的 Web 伺服器,開啟長連線,提升 TCP 的傳輸效率;

  9. 前端應該啟用 gzip、br 壓縮,減小文字、圖片的體積,儘量少傳不必要的頭欄位;

  10. 快取是無論何時都不能忘記的效能優化利器,應該總使用 Etag 或 Last-modified 欄位標記資源;

  11. 升級到 HTTP/2 能夠直接獲得許多方面的效能提升,但要留意一些 HTTP/1 的“反模式”。

 

參考文章

透視HTTP協