點擊上方藍色字體,選擇「標星公衆號」
php
優質文章,第一時間送達前端
做者 | huanskyjava
來源 | urlify.cn/3E773eweb
本文主要是側重於 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」(Win十、iOS九、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 圖片合併,數據內嵌),讓資源的粒度儘量地小,才能更好地發揮緩存的做用。
小結
性能優化是一個複雜的概念,在 HTTP 裏能夠分解爲服務器性能優化、客戶端性能優化和傳輸鏈路優化;
服務器有三個主要的性能指標:吞吐量、併發數和響應時間,此外還須要考慮資源利用率;
客戶端的基本性能指標是延遲,影響因素有地理距離、帶寬、DNS 查詢、TCP 握手等;
從服務器到客戶端的傳輸鏈路能夠分爲三個部分,咱們可以優化的是前兩個部分,也就是「第一千米」和「中間一千米」;
有不少工具能夠測量這些指標,服務器端有 ab、top、sar 等,客戶端可使用測試網站,瀏覽器的開發者工具。
花錢購買硬件、軟件或者服務能夠直接提高網站的服務能力,其中最有價值的是 CDN;
不花錢也能夠優化 HTTP,三個關鍵詞是「開源」「節流」和「緩存」;
後端應該選用高性能的 Web 服務器,開啓長鏈接,提高 TCP 的傳輸效率;
前端應該啓用 gzip、br 壓縮,減少文本、圖片的體積,儘可能少傳沒必要要的頭字段;
緩存是不管什麼時候都不能忘記的性能優化利器,應該總使用 Etag 或 Last-modified 字段標記資源;
升級到 HTTP/2 可以直接得到許多方面的性能提高,但要留意一些 HTTP/1 的「反模式」。
粉絲福利:Java從入門到入土學習路線圖
👇👇👇
👆長按上方微信二維碼 2 秒
感謝點贊支持下哈
本文分享自微信公衆號 - java1234(gh_27ed55ecb177)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。