HTTP/2特性及其在實際應用中的表現

美團點評點餐發表於2017-10-16

引言

HTTP/2是於2015年正式釋出的新一代HTTP協議。其在保持HTTP1.x語義、不變更網路基礎設施的前提下致力於降低使用者可感知延時,提升網路傳輸效率。本文將從HTTP歷史、HTTP/2新特性、升級方法、效果分析四個部分進行介紹。主要涉及了為什麼我們需要HTTP/2,HTTP/2能帶給我們什麼,如何升級,當前HTTP/2在實際應用的表現怎樣四個問題。

HTTP歷史

自從1991年Tim Berners-Lee提出HTTP協議的設想到現在已經20多年過去了,在這20多年中Web的發展也是日新月異。為了滿足不同時代Web的需求,HTTP協議的更新迭代經歷了HTTP0.9、HTTP/1.0、HTTP/1.1、SPDY、HTTP/2幾個版本。接下來就這幾個版本的HTTP進行簡要介紹。 HTTP/2特性及其在實際應用中的表現

HTTP 0.9 (1991)

HTTP/0.9產生於Web發展的萌芽階段,是一個簡單到只有一條請求行(request line)的HTTP協議。HTTP/0.9只支援GET POST HEAD請求,請求行中不不包含版本號(那時也沒有別的版本)。在發起請求、伺服器響應(僅支援純文字,沒有響應頭)後連線即會關閉。現在流行的Web伺服器依然支援HTTP/0.9,因為支援它實在花不了多大代價。

GET /index
(響應)
(連線關閉)複製程式碼

HTTP/1.0

1991-1995年,隨著Web瀏覽器的興起,人們對Web頁面的需求越來越多,最典型的就是人們不再滿足於僅包含文字和超連結的超文字文件,而是需要能展現文字、樣式、圖片等多種媒體型別的資料。因此HTTP/0.9這種簡單的傳輸協議很快就難以為繼。1996年,HTTP-WG釋出了了RFC1945,闡述了HTTP/1.0的主要特性:

  • 請求和響應有多個頭部,請求行包含了協議版本
  • 包含了響應行,主要包括協議版本和響應狀態
  • 響應頭中包含了Content-Type,可以支援多種型別的影響內容
  • 每個請求響應結束後,連線即關閉

其實,從現在開始HTTP協議包含的內容已經超出了其名字(超文字傳輸)所指代的範圍,但是這個名字仍然沿用了下來。

/* 請求 */
GET /rfc/rfc1945.txt HTTP/1.0
User-Agent: CERN-LineMode/2.15 libwww/2.17b3 Accept: */* /* 響應 */
HTTP/1.0 200 OK
ontent-Type: text/plain
Content-Length: 137582
Expires: Thu, 01 Dec 1997 16:00:00 GMT Last-Modified: Wed, 1 May 1996 12:45:26 GMT Server: Apache 0.84複製程式碼

HTTP/1.1

1997年定義HTTP/1.1的RFC2068正式釋出,隨後在1999年釋出的RFC2616中集合了對HTTP/1.1的改進和更新。總的來講,HTTP/1.1主要明確了之前HTTP/1.0中存在歧義的點,並在此基礎上增加了許多新特性:

  • 持久傳輸
  • 分塊編碼傳輸
  • 位元組範圍請求
  • 傳輸編碼
  • 增強的快取機制
  • 管道化請求
// 請求1
GET /index.html HTTP/1.1
Host: website.org
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_4)... (snip)

// 響應1
HTTP/1.1 200 OK
Server: nginx/1.0.11
Accept-Ranges: bytes // 如果不為none,代表server支援範圍請求
Connection: keep-alive // 持久傳輸,本次請求響應結束後不關閉連線
Content-Type: text/html; charset=utf-8
Cache-Control: max-age=0, no-cache // 快取控制
Transfer-Encoding: chunked // 傳輸編碼

100 // 當前響應塊大小(256Bytes)
<!doctype html> ... (snip) // 響應內容

100 // 另一塊(256Bytes)
... (snip) // 響應內容

0 // 響應分塊結束

// 請求2
GET /favicon.ico HTTP/1.1 // 上次請求未關閉連線,本次重用該連線
Range: bytes=0-1023 // 請求位元組範圍
Connection: close // 告知伺服器本次請求響應後關閉連線

// 響應2
HTTP/1.1 200 OK // 第二次請求的響應行
Content-Range: bytes 0-1023/146515 // 響應位元組範圍//整體位元組數
Connection: close // 告知瀏覽器響應完成或關閉該連線複製程式碼

SPDY & HTTP/2

從誕生之日到今天,HTTP/1.1以其頑強的生命力伴隨一代人見證了Web的繁榮發展。但面對網路中越來越多的請求、網頁規模的膨脹,HTTP/1.1逐漸開始表現得力不從心,主要表現在以下幾個問題上:

  • 頭阻塞:HTTP/1.1中只有該請求響應後才能重用當前連線傳送下一次請求,即使管道化技術使得可以在本次響應完成之前傳送下次請求,但響應依然嚴格按照順序返回,也就是如果前一個響應被阻塞,後邊的響應將不會到來。
  • 重複的未壓縮頭資料傳輸:自HTTP/1.0以後,HTTP請求中通常帶有大量以ASCII編碼的頭部,這些頭部通常大部分都不會變化,每次請求都會攜帶,這給本就擁擠不堪的網路帶來了更多的壓力(尤其是像User Agent、Cookie這種值比較長的頭部欄位)。

2009年,Google提出了一項實驗性的協議SPDY(讀音同speedy),旨在開發者不修改當前網站實現的前提下,提高頁面載入速度。SPDY提出後,Chrome、Firefox、Opera等主流瀏覽器先後給出了實現,很多大型網站(如Google、Twitter、Facebook等)分別提供了其對SPDY會話的實現。2012年,HTTP-WG提出了在SPDY基礎上構建HTTP/2的草案,2013年給出了第一個對HTTP/2的實現,自此HTTP/2、SPDY並行發展,在客戶端和伺服器上進行了廣泛可靠的測試。2015年,Google 宣佈放棄對SPDY的繼續支援,標誌著HTTP/2正式登上歷史舞臺。

HTTP/2 新特性

二進位制幀層與多路複用

與HTTP/1.x採用ASCII碼明文傳輸的方式不同,HTTP/2在伺服器和客戶端之間採用傳輸效率更高的二進位制的形式。值得一提的是,HTTP/2中報文二進位制幀被分為頭部幀(Header Frame)和資料幀(Data Frame),這主要是因為HTTP/2為頭部欄位單獨提供了一種壓縮方式,可以使網路中傳輸的頭部資料降低到最小(見下一節)。 HTTP/2特性及其在實際應用中的表現在HTTP/2中,所有資料的傳輸可以分為連線(Connection)、流(Stream)和幀(Frame)三個層次。與HTTP/1.1中採用的並行連線不同,HTTP/2在客戶端和伺服器的整個會話都會複用一條連線。在這個連線中一組完整地請求、響應被稱為流。流中的每段資料(頭部、資料段)被稱為幀。幀是HTTP/2中通訊的最小單位,其中包含了其屬於那條流的“身份”資訊。 HTTP/2特性及其在實際應用中的表現在HTTP/1.x中,通常採用維持多個連線的方式使請求和響應並行傳送,因為計算和儲存資源的限制,無法通過無限開啟新連線(通常上限為六個)的方式徹底解決頭阻塞問題。在HTTP/2中,所有的資料通訊均在一個連線中完成。每個資料幀在傳送端準備完成後即可傳送,無需等待前一個請求的響應。資料接收端則在接到這些亂序傳送的資料幀後,再根據每個幀攜帶的“身份”,對這些資料進行重新組裝,以獲得完整地請求或響應資料。HTTP/2中採用的多路複用技術徹底解決了頭阻塞問題;重複利用了每一組請求、響應之間的連線的網路資源,在降低資源開銷的同時,降低了資料傳輸延時。 HTTP/2特性及其在實際應用中的表現

頭部壓縮(Hpack)

前面已經提到,HTTP/1.x會在請求和響應中中重複地攜帶不常改變的、冗長的頭部資料,給網路帶來不必要地負擔。HTTP/2除了將ASCII明文轉化為二進位制幀以提高網路傳輸的有效性外,還提出了一種專門針對報文頭部資料進行壓縮的方案——Hpack。其主要思想是,通過重用當前連線中之前傳送請求中的頭部,僅傳送新加或更改的頭部資訊,以降低報文中頭部欄位大小。頭部壓縮的過程如下圖所示:

  • 連線會維持一個查詢表,該查詢表中給出了62個常用欄位及其預設值(來自對多個常用網站中請求的統計)。
  • 在第一次請求時,會首先檢視查詢表中對應欄位的值是否為本次請求攜帶的值,如果不是則更改這個查詢表,同時增加查詢表中沒有的欄位。在查詢表中將所有頭部新增完成後才根據查詢表對頭部進行壓縮併傳送請求。
  • 在接下來的每次請求中(當前連線),報文中的壓縮資料僅包含查詢表中被修改的欄位。鑑於一個連線中很多請求中大部分頭部欄位攜帶的值都是不常變化的,此舉可以大大降低頭部傳輸所需的網路資源。 HTTP/2特性及其在實際應用中的表現

流的優先順序

在HTTP/2中,請求和響應是可以亂序傳輸的,因此我們需要一個機制可以確保哪些被其他響應資料所依賴的或者關鍵資源被優先傳輸,以使網頁的呈現和使用具有最好的體驗。HTTP/2中在流的層面,採用了“優先順序樹”的形式確保響應資料能夠按照依賴關係和優先順序順序來傳輸。

“優先順序樹”可以表示成如下圖所示的樣子。其中子節點所表示的流響應依賴於父節點流,因此父節點流應該被優先傳輸。在兄弟節點中,被分配權重較大的點應該被分配更多的網路資源,被優先傳輸。 如下圖中(3)所示的“優先順序樹”中,A,B依賴於C,C依賴於D,因此D應該被優先傳輸,D傳輸完後才應該傳輸C,同理C傳輸完成後才應該傳輸A,B,A,B的資料在傳輸過程中所佔用的網路資源應遵循3:1的關係。 HTTP/2特性及其在實際應用中的表現

Server Push

簡單來講,Server Push就是允許Server對還沒有發出的請求進行響應。如下圖所示,在第一個請求中,客戶端發出了一條對index.html的請求。通過對index.html的分析,伺服器可以推測出其該html的載入還需要style.css和script.js,因此在第一條請求的響應中可以將這些內容一併傳送過去,避免了接下來兩條請求、響應帶來的延遲(RTT, Round-trip Time)。雖然在HTTP/1.x中採用inline的方式,也可以達到類似Server Push的效果,但inline具有以下弊端:

  • CSS或js沒有單獨的快取,如果html的快取方式與被inline的內容不一致,在之後對該頁面訪問時,本來可以快取的css或js內容被重複請求載入
  • 其他頁面用到該樣式會被重複請求載入 HTTP/2特性及其在實際應用中的表現

升級到HTTP/2

在升級之前首先要確認你當前使用的客戶端(瀏覽器)和伺服器均支援了HTTP/2。幸運的是,迄今為止絕大多數瀏覽器均已對HTTP/2會話提供了支援,目前為止主流的Web伺服器最新版本已經可以支援HTTP/2(Nginx ^1.9.5, Apache ^2.4.24 mod_http2)。所以僅需要安裝最新版本的伺服器即可使用其提供的HTTP/2新特性。需要注意的是,在升級HTTP/2之前要確認伺服器支援SSL(支援HTTPS)。HTTP/2自身並不要求必須底層支援SSL,但當伺服器不支援SSL時,幾乎所有的瀏覽器會無視HTTP/2連線。 HTTP/2特性及其在實際應用中的表現

去除某些針對HTTP/1.x的優化

在HTTP/1.x中存在一些用於Web效能提升的“奇技淫巧”,但這些技術可能在HTTP/2中起到相反的作用,因此我們第一步就是先去除這些可能會妨礙效能的優化。

域名分片(domain sharding)

HTTP/1.x中,通訊兩端最多隻有六個連線,且是通過區分不同域名來維持管理的。為了突破這個限制,通常會把請求資源至於不同的域名下(如 shard1.example.org, shard2.example.org)。而在HTTP/2中,因為不需要新開連線來解決頭阻塞問題,所以不需要通過這種方式來增加通訊的連線數。相反的在HTTP/2中採用域名分片會造成以下兩個問題:

  • 增加DNS域名解析時間
  • 增加傳輸中壓縮頭部的大小。前文已經提到,頭部壓縮中的資料複用是在一個連線上維持的,域名分片後新開的連線無法複用之前已經傳送過的頭部資料,造成一些不必要地資料在網路上的傳輸。

雪碧圖

HTTP/1.x中,為了減少請求多個圖片帶來的頭阻塞問題,通常採用把多個圖片拼接成一個大圖,然後一個請求將所有圖片載入在瀏覽器中,然後使用CSS技術將所需要的部分按需展示出來。顯然的,雪碧圖會帶來如下問題:

  • 增加程式碼量,需要寫一些本不必要地CSS程式碼
  • 在瀏覽器渲染過程中,記憶體中需要載入更多的圖片內容

拼接JavaScript CSS

同雪碧圖的原理一樣,拼接JavaScript、CSS的做法同樣是為了減少請求數,以避免潛在的頭阻塞問題。在HTTP/2中,因為不存在頭阻塞問題,因此應該避免這種優化方式帶來的一些不必要地資源的載入。

升級

根據不同的業務型別和複雜程度,可以選擇不同的升級方式,這裡給出三種升級HTTP/2的方式。

將靜態資源存放到支援HTTP/2的CDN中

通常情況下,一個網站中絕大多數的請求是針對靜態資源發起的,因此將靜態資源部署到支援HTTP/2的CDN上是一種最方便快捷的升級方法。在這種情況下訪問靜態資源的請求通過HTTP/2完成,而API訪問則通過HTTP/1.x完成。這種方法雖然比較便捷,但由於對服務端HTTP/2的支援失去控制,往往難以充分利用所有已經實現的新特性(如Server Push、優先順序控制等)。 HTTP/2特性及其在實際應用中的表現

升級反向代理伺服器

反向代理伺服器位於客戶端和真正的伺服器之間,用於對高併發訪問的伺服器進行負載均衡。在這種情況下頭阻塞主要發生在客戶端和代理伺服器之間的通訊,因此在代理伺服器中支援HTTP/2可以消除大部分頭阻塞帶來的效能問題。而代理伺服器和真正的伺服器之間仍然採用HTTP/1.x進行通訊。 HTTP/2特性及其在實際應用中的表現

伺服器完全升級

當然,如果要充分利用HTTP/2帶來的效能提升,就需要對所有的伺服器啟用支援,以保證客戶端和伺服器之間的所有通訊均採用HTTP/2執行。值得注意的是,現在HTTP/2中的很多特性仍處於實驗階段,不同的伺服器、包可能對不同特性的支援略有差別,所以在升級之前需要充分閱讀文件,確保你需要的特性已經被支援。 HTTP/2特性及其在實際應用中的表現

案例分析

case1

Akamai公司給出了一個測試頁面用於對比在存在大量請求情況下HTTP/2與HTTP/1.x之間巨大的效能的差異。通過檢視HTTP/2請求與HTTP/1.x請求的瀑布流可以發現,HTTP/1.x連線存在嚴重的頭阻塞問題,每個時刻最多隻可能有6條請求在6條連線上執行,而HTTP/2採用多條請求複用一個連線的機制,同一時刻可以接收到的請求數不受連線數的限制,能更加充分地利用網路頻寬。在筆者的網路環境下,後者可以將載入同樣資源的時間降低50倍。雖然這是一個比較極端的案例,但HTTP/2帶來的效能提升可見一斑。 HTTP/2特性及其在實際應用中的表現 HTTP/2特性及其在實際應用中的表現HTTP/2特性及其在實際應用中的表現

case2

Dropbox提供了其Web伺服器從SPDY升級到HTTP/2(Niginx 1.9.15)的過程及分析。下圖給出其在升級過程中,SPDY連線和HTTP/2連線的佔比情況,可以看到起其升級過程是在多個伺服器中逐步完成的,這給我們對比升級前後的效能提供了很好地素材。 HTTP/2特性及其在實際應用中的表現 下圖給出了在升級過程中,請求(ingress)和響應(egress)所佔據網路頻寬相對於升級前平均值的比。可以看到在切換後,請求所佔據網路頻寬有了較大幅度的下降(約為原來的一半),這主要是由HTTP/2的頭部壓縮帶來的(雖然SPDY也提供了頭部壓縮的特性,但由於存在安全問題,通常都不會啟用)。而響應占據的網路資源沒有太大變化,這主要是因為頭部資料在響應中僅佔很小一部分。 HTTP/2特性及其在實際應用中的表現 在升級中也發現了一些存在的問題。通過下圖可以看出對於POST請求來說,平均延時上升了50%。這是由於Nginx 1.9.15對HTTP/2的支援缺陷造成的。具體來講,Nginx會將SETTINGS.INITIAL_WINDOW_SIZE欄位設為0,這意味著在POST請求在完成TCP握手後並不能立即傳送資料,而是需要一個RTT等待伺服器將該值提升了一個較大的值後才能開始傳送資料(細節參見TCP的慢啟動機制)。雖然Nginx 1.11.0修復了這個問題,這依然提示我們在升級HTTP/2之前,需要充分調研所要採用的伺服器和對應的包目前存在的問題,以避免升級可能帶來的效能下降。 HTTP/2特性及其在實際應用中的表現

case3

99design網站也對其向HTTP/2遷移做了詳細的總結。其遷移工作主要是通過將圖片資源部署到支援HTTP/2的CDN上來實現的(升級方法1),衡量效能的指標是頁面視覺呈現時間。通過對不同頁面的測試可以發現以下三個現象(關於延時受限和頻寬受限的區別可以參考這裡):

  • 在延時受限的場景中,升級後的頁面視覺呈現速度提升了5%左右 HTTP/2特性及其在實際應用中的表現
  • 在頻寬受限的場景中,升級後的頁面視覺呈現速度反而會降低15% HTTP/2特性及其在實際應用中的表現
  • 在頻寬和延時均受限的場景中,升級後的頁面視覺呈現速度會進一步下降,甚至會達到25%以上(下面第一個圖為http/1.1,第二個圖為HTTP/2)

HTTP/2特性及其在實際應用中的表現 HTTP/2特性及其在實際應用中的表現

通過分析得出效能的下降的原因是升級HTTP/2後前端程式碼失去了載入資源優先順序的控制,從而造成一些影響視覺呈現的關鍵資源被後置載入導致整個視覺呈現時間的拉長。具體來講,在HTTP/1.x中,先發起請求的資源通常會先載入。因此我們可以在前端控制資源的載入優先順序,將對於視覺呈現較為關鍵的資源進行優先載入。但在HTTP/2中,響應的返回是亂序的,一些非關鍵的資源可能會被先返回。當頻寬不是限制因素時,這自然不是問題。但當這些非關鍵資源將頻寬耗盡時,關鍵資源就只好等待頻寬釋放後才能返回。一言以蔽之,在HTTP/2的使用使得對資源載入順序的控制權從前端轉移到後端了。很顯然,其採用的CDN是不支援對流的優先順序傳輸的。這也印證了前文中提到的,使用CDN升級HTTP/2的方式可能會讓我們無法使用HTTP/2的一些特性,甚至會完全失去控制。

總結

最後,總結一下。

為什麼要用HTTP/2?

HTTP/1.x中的頭阻塞問題會造成連線總是處於等待響應的狀態,而未充分利用頻寬;HTTP/1.x大量、重複的請求頭在網路上傳輸,使網路負載了很大一部分本需要傳輸的資料量。

HTTP/2能帶給我們什麼?

除了解決了上述HTTP/1.x的問題,新的協議還包含了Server Push、流的優先順序傳輸等新特性,使效能得到進一步提升。

如何升級?

目前的主流瀏覽器均已支援,因此一般情況下只需要升級服務端即可。三種方案:找一個支援HTTP/2的CDN部署網站中的靜態資源;升級代理伺服器;升級所有伺服器。

HTTP/2實際表現如何?

在存在大量請求、延時受限的環境下,HTTP/2的效能表現相較於HTTP/1.x有明顯優勢。但考慮到HTTP/2尚年幼,很多特性的實現還未完善,許多特性的表現可能並非理想。因此線上上升級之前要做充分的測試,確保所採用的實現能夠支援你需的特性,其缺陷不會對效能造成重大影響。

【參考文獻】


相關文章