HTTP/2是於2015年正式發佈的新一代HTTP協議。其在保持HTTP1.x語義、不變動網絡基礎設施的前提下致力於下降用戶可感知延時,提高網絡傳輸效率。本文將從HTTP歷史、HTTP/2新特性、升級方法、效果分析四個部分進行介紹。主要涉及了爲何咱們須要HTTP/2,HTTP/2能帶給咱們什麼,如何升級,當前HTTP/2在實際應用的表現怎樣四個問題。css
自從1991年Tim Berners-Lee提出HTTP協議的設想到如今已經20多年過去了,在這20多年中Web的發展也是突飛猛進。爲了知足不一樣時代Web的需求,HTTP協議的更新迭代經歷了HTTP0.九、HTTP/1.0、HTTP/1.一、SPDY、HTTP/2幾個版本。接下來就這幾個版本的HTTP進行簡要介紹。 html
HTTP/0.9產生於Web發展的萌芽階段,是一個簡單到只有一條請求行(request line)的HTTP協議。HTTP/0.9只支持GET POST HEAD請求,請求行中不不包含版本號(那時也沒有別的版本)。在發起請求、服務器響應(僅支持純文本,沒有響應頭)後鏈接即會關閉。如今流行的Web服務器依然支持HTTP/0.9,由於支持它實在花不了多大代價。前端
GET /index
(響應)
(鏈接關閉)複製代碼
1991-1995年,隨着Web瀏覽器的興起,人們對Web頁面的需求愈來愈多,最典型的就是人們再也不知足於僅包含文本和超連接的超文本文檔,而是須要能展示文字、樣式、圖片等多種媒體類型的數據。所以HTTP/0.9這種簡單的傳輸協議很快就難覺得繼。1996年,HTTP-WG發佈了了RFC1945,闡述了HTTP/1.0的主要特性:nginx
其實,從如今開始HTTP協議包含的內容已經超出了其名字(超文本傳輸)所指代的範圍,可是這個名字仍然沿用了下來。web
/* 請求 */
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複製代碼
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 // 告知瀏覽器響應完成或關閉該鏈接複製代碼
從誕生之日到今天,HTTP/1.1以其頑強的生命力伴隨一代人見證了Web的繁榮發展。但面對網絡中愈來愈多的請求、網頁規模的膨脹,HTTP/1.1逐漸開始表現得力不從心,主要表如今如下幾個問題上:瀏覽器
2009年,Google提出了一項實驗性的協議SPDY(讀音同speedy),旨在開發者不修改當前網站實現的前提下,提升頁面加載速度。SPDY提出後,Chrome、Firefox、Opera等主流瀏覽器前後給出了實現,不少大型網站(如Google、Twitter、Facebook等)分別提供了其對SPDY會話的實現。2012年,HTTP-WG提出了在SPDY基礎上構建HTTP/2的草案,2013年給出了第一個對HTTP/2的實現,自此HTTP/二、SPDY並行發展,在客戶端和服務器上進行了普遍可靠的測試。2015年,Google 宣佈放棄對SPDY的繼續支持,標誌着HTTP/2正式登上歷史舞臺。緩存
與HTTP/1.x採用ASCII碼明文傳輸的方式不一樣,HTTP/2在服務器和客戶端之間採用傳輸效率更高的二進制的形式。值得一提的是,HTTP/2中報文二進制幀被分爲頭部幀(Header Frame)和數據幀(Data Frame),這主要是由於HTTP/2爲頭部字段單獨提供了一種壓縮方式,可使網絡中傳輸的頭部數據下降到最小(見下一節)。 在HTTP/2中,全部數據的傳輸能夠分爲鏈接(Connection)、流(Stream)和幀(Frame)三個層次。與HTTP/1.1中採用的並行鏈接不一樣,HTTP/2在客戶端和服務器的整個會話都會複用一條鏈接。在這個鏈接中一組完整地請求、響應被稱爲流。流中的每段數據(頭部、數據段)被稱爲幀。幀是HTTP/2中通訊的最小單位,其中包含了其屬於那條流的「身份」信息。 在HTTP/1.x中,一般採用維持多個鏈接的方式使請求和響應並行發送,由於計算和存儲資源的限制,沒法經過無限開啓新鏈接(一般上限爲六個)的方式完全解決頭阻塞問題。在HTTP/2中,全部的數據通訊均在一個鏈接中完成。每一個數據幀在發送端準備完成後便可發送,無需等待前一個請求的響應。數據接收端則在接到這些亂序發送的數據幀後,再根據每一個幀攜帶的「身份」,對這些數據進行從新組裝,以得到完整地請求或響應數據。HTTP/2中採用的多路複用技術完全解決了頭阻塞問題;重複利用了每一組請求、響應之間的鏈接的網絡資源,在下降資源開銷的同時,下降了數據傳輸延時。 安全
前面已經提到,HTTP/1.x會在請求和響應中中重複地攜帶不常改變的、冗長的頭部數據,給網絡帶來沒必要要地負擔。HTTP/2除了將ASCII明文轉化爲二進制幀以提升網絡傳輸的有效性外,還提出了一種專門針對報文頭部數據進行壓縮的方案——Hpack。其主要思想是,經過重用當前鏈接中以前發送請求中的頭部,僅發送新加或更改的頭部信息,以下降報文中頭部字段大小。頭部壓縮的過程以下圖所示:bash
在HTTP/2中,請求和響應是能夠亂序傳輸的,所以咱們須要一個機制能夠確保哪些被其餘響應數據所依賴的或者關鍵資源被優先傳輸,以使網頁的呈現和使用具備最好的體驗。HTTP/2中在流的層面,採用了「優先級樹」的形式確保響應數據可以按照依賴關係和優先級順序來傳輸。
「優先級樹」能夠表示成以下圖所示的樣子。其中子節點所表示的流響應依賴於父節點流,所以父節點流應該被優先傳輸。在兄弟節點中,被分配權重較大的點應該被分配更多的網絡資源,被優先傳輸。 以下圖中(3)所示的「優先級樹」中,A,B依賴於C,C依賴於D,所以D應該被優先傳輸,D傳輸完後才應該傳輸C,同理C傳輸完成後才應該傳輸A,B,A,B的數據在傳輸過程當中所佔用的網絡資源應遵循3:1的關係。
簡單來說,Server Push就是容許Server對尚未發出的請求進行響應。以下圖所示,在第一個請求中,客戶端發出了一條對index.html的請求。經過對index.html的分析,服務器能夠推測出其該html的加載還須要style.css和script.js,所以在第一條請求的響應中能夠將這些內容一併發送過去,避免了接下來兩條請求、響應帶來的延遲(RTT, Round-trip Time)。雖然在HTTP/1.x中採用inline的方式,也能夠達到相似Server Push的效果,但inline具備如下弊端:
在升級以前首先要確認你當前使用的客戶端(瀏覽器)和服務器均支持了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/1.x中存在一些用於Web性能提高的「奇技淫巧」,但這些技術可能在HTTP/2中起到相反的做用,所以咱們第一步就是先去除這些可能會妨礙性能的優化。
HTTP/1.x中,通訊兩端最多隻有六個鏈接,且是經過區分不一樣域名來維持管理的。爲了突破這個限制,一般會把請求資源至於不一樣的域名下(如 shard1.example.org, shard2.example.org)。而在HTTP/2中,由於不須要新開鏈接來解決頭阻塞問題,因此不須要經過這種方式來增長通訊的鏈接數。相反的在HTTP/2中採用域名分片會形成如下兩個問題:
HTTP/1.x中,爲了減小請求多個圖片帶來的頭阻塞問題,一般採用把多個圖片拼接成一個大圖,而後一個請求將全部圖片加載在瀏覽器中,而後使用CSS技術將所須要的部分按需展現出來。顯然的,雪碧圖會帶來以下問題:
同雪碧圖的原理同樣,拼接JavaScript、CSS的作法一樣是爲了減小請求數,以免潛在的頭阻塞問題。在HTTP/2中,由於不存在頭阻塞問題,所以應該避免這種優化方式帶來的一些沒必要要地資源的加載。
根據不一樣的業務類型和複雜程度,能夠選擇不一樣的升級方式,這裏給出三種升級HTTP/2的方式。
一般狀況下,一個網站中絕大多數的請求是針對靜態資源發起的,所以將靜態資源部署到支持HTTP/2的CDN上是一種最方便快捷的升級方法。在這種狀況下訪問靜態資源的請求經過HTTP/2完成,而API訪問則經過HTTP/1.x完成。這種方法雖然比較便捷,但因爲對服務端HTTP/2的支持失去控制,每每難以充分利用全部已經實現的新特性(如Server Push、優先級控制等)。
反向代理服務器位於客戶端和真正的服務器之間,用於對高併發訪問的服務器進行負載均衡。在這種狀況下頭阻塞主要發生在客戶端和代理服務器之間的通訊,所以在代理服務器中支持HTTP/2能夠消除大部分頭阻塞帶來的性能問題。而代理服務器和真正的服務器之間仍然採用HTTP/1.x進行通訊。
固然,若是要充分利用HTTP/2帶來的性能提高,就須要對全部的服務器啓用支持,以保證客戶端和服務器之間的全部通訊均採用HTTP/2執行。值得注意的是,如今HTTP/2中的不少特性仍處於實驗階段,不一樣的服務器、包可能對不一樣特性的支持略有差異,因此在升級以前須要充分閱讀文檔,確保你須要的特性已經被支持。
Akamai公司給出了一個測試頁面用於對比在存在大量請求狀況下HTTP/2與HTTP/1.x之間巨大的性能的差別。經過查看HTTP/2請求與HTTP/1.x請求的瀑布流能夠發現,HTTP/1.x鏈接存在嚴重的頭阻塞問題,每一個時刻最多隻可能有6條請求在6條鏈接上執行,而HTTP/2採用多條請求複用一個鏈接的機制,同一時刻能夠接收到的請求數不受鏈接數的限制,能更加充分地利用網絡帶寬。在筆者的網絡環境下,後者能夠將加載一樣資源的時間下降50倍。雖然這是一個比較極端的案例,但HTTP/2帶來的性能提高可見一斑。
Dropbox提供了其Web服務器從SPDY升級到HTTP/2(Niginx 1.9.15)的過程及分析。下圖給出其在升級過程當中,SPDY鏈接和HTTP/2鏈接的佔比狀況,能夠看到起其升級過程是在多個服務器中逐步完成的,這給咱們對比升級先後的性能提供了很好地素材。 下圖給出了在升級過程當中,請求(ingress)和響應(egress)所佔據網絡帶寬相對於升級前平均值的比。能夠看到在切換後,請求所佔據網絡帶寬有了較大幅度的降低(約爲原來的一半),這主要是由HTTP/2的頭部壓縮帶來的(雖然SPDY也提供了頭部壓縮的特性,但因爲存在安全問題,一般都不會啓用)。而響應占據的網絡資源沒有太大變化,這主要是由於頭部數據在響應中僅佔很小一部分。 在升級中也發現了一些存在的問題。經過下圖能夠看出對於POST請求來講,平均延時上升了50%。這是因爲Nginx 1.9.15對HTTP/2的支持缺陷形成的。具體來說,Nginx會將SETTINGS.INITIAL_WINDOW_SIZE
字段設爲0,這意味着在POST請求在完成TCP握手後並不能當即發送數據,而是須要一個RTT等待服務器將該值提高了一個較大的值後才能開始發送數據(細節參見TCP的慢啓動機制)。雖然Nginx 1.11.0修復了這個問題,這依然提示咱們在升級HTTP/2以前,須要充分調研所要採用的服務器和對應的包目前存在的問題,以免升級可能帶來的性能降低。
99design網站也對其向HTTP/2遷移作了詳細的總結。其遷移工做主要是經過將圖片資源部署到支持HTTP/2的CDN上來實現的(升級方法1),衡量性能的指標是頁面視覺呈現時間。經過對不一樣頁面的測試能夠發現如下三個現象(關於延時受限和帶寬受限的區別能夠參考這裏):
經過分析得出性能的降低的緣由是升級HTTP/2後前端代碼失去了加載資源優先級的控制,從而形成一些影響視覺呈現的關鍵資源被後置加載致使整個視覺呈現時間的拉長。具體來說,在HTTP/1.x中,先發起請求的資源一般會先加載。所以咱們能夠在前端控制資源的加載優先級,將對於視覺呈現較爲關鍵的資源進行優先加載。但在HTTP/2中,響應的返回是亂序的,一些非關鍵的資源可能會被先返回。當帶寬不是限制因素時,這天然不是問題。但當這些非關鍵資源將帶寬耗盡時,關鍵資源就只好等待帶寬釋放後才能返回。一言以蔽之,在HTTP/2的使用使得對資源加載順序的控制權從前端轉移到後端了。很顯然,其採用的CDN是不支持對流的優先級傳輸的。這也印證了前文中提到的,使用CDN升級HTTP/2的方式可能會讓咱們沒法使用HTTP/2的一些特性,甚至會徹底失去控制。
最後,總結一下。
HTTP/1.x中的頭阻塞問題會形成鏈接老是處於等待響應的狀態,而未充分利用帶寬;HTTP/1.x大量、重複的請求頭在網絡上傳輸,使網絡負載了很大一部分本須要傳輸的數據量。
除了解決了上述HTTP/1.x的問題,新的協議還包含了Server Push、流的優先級傳輸等新特性,使性能獲得進一步提高。
目前的主流瀏覽器均已支持,所以通常狀況下只須要升級服務端便可。三種方案:找一個支持HTTP/2的CDN部署網站中的靜態資源;升級代理服務器;升級全部服務器。
在存在大量請求、延時受限的環境下,HTTP/2的性能表現相較於HTTP/1.x有明顯優點。但考慮到HTTP/2尚年幼,不少特性的實現還未完善,許多特性的表現可能並不是理想。所以在線上升級以前要作充分的測試,確保所採用的實現可以支持你需的特性,其缺陷不會對性能形成重大影響。