大家都在用HTTP/2了,而你還沒聽說過?

liujiusheng發表於2018-12-29

自2013年HTTP/2推出以來,HTTP/2已經得到了長足發展,瀏覽器平臺基本提供了HTTP/2的支援,知乎,豆瓣等國內大型平臺也已經部分切換到HTTP/2了。

HTTP/2優點

多路複用請求 對請求劃分優先順序 壓縮HTTP頭 伺服器推送流

HTTP/2支援情況:

chrome,firefox,ie11(win10版)都已經支援

詳情可檢視:caniuse.com/#search=htt…

HTTP/2快取情況:

HTTP/2並不像我們所在網上搜尋到的那樣不能快取,只是需要在伺服器端配置快取,如:"expires":"Tue, 09 Oct 2018 11:27:20 GMT"。

測試方法:

在地圖中應用時,載入圖片有兩種方式,一種是直接在img例項上設定url,讓瀏覽器自己去觸發訪問圖片,另一種是通過ajax載入(xhr.responseType = 'blob';,因為ajax請求過來的都是二進位制字串,所以要解析,後端讀取檔案和返回檔案內容時同樣要設定引數為binary),請求的結果通過img.src = window.URL.createObjectURL(data);解析成圖片url。

根據兩種載入方式,我構造了四組測試程式碼,並分別選取了每個圖片大小為237KB和2KB的大小兩組圖片進行測試:

  1. HTTP/2&&AJAX載入
  2. HTTP/2&&瀏覽器載入
  3. http1.1&&AJAX載入
  4. http1.1&&瀏覽器載入

其它注意事項

使用瀏覽器訪問的時候一定要用https協議訪問,http協議訪問不到

chrome允許本地https,進入chrome://flags/#allow-insecure-localhost設定其值為enabled

chrome裡network內列標題上右鍵可開啟protocol列檢視請求的協議是不是h2

初次載入時會自動載入favicon.ico檔案,可能會導致出錯

程式碼實現

localhost-cert.pem

-----BEGIN CERTIFICATE-----
MIIC+zCCAeOgAwIBAgIJALUOjGkvsHSZMA0GCSqGSIb3DQEBCwUAMBQxEjAQBgNV
BAMMCWxvY2FsaG9zdDAeFw0xODAxMjUwNTMxMDFaFw0xODAyMjQwNTMxMDFaMBQx
EjAQBgNVBAMMCWxvY2FsaG9zdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC
ggEBANRicexesNtTbjVnxKyEWalFovZUEYQ/FRjArJAndaEBSVlsrTRFrp3nEUNx
6O6EUM4SrfOTCfEW6Pi9YqH8wVk8/G3BCOG99as6GIHabkbC79UiNdDhAQLr10+g
gaXl+QYxvGEvk/Qb/9LRFc3EqurVNmKdEPVUUpgcR8dL9SnOguLoSuKA2U+5AYLm
lX0lUG2S34o+H+Ck6dC8IK94uFNpyNPvd4xf+xEERBm7Q5IVLjZ0D40luF1g7S9L
IltT8BTYeYeCWGoNifQccS/5qfwuK3C7ga29lduJKNmDzhiKCCTelU92+COYisiE
sgYK4fbFnGHbiETFu87IJsABD2kCAwEAAaNQME4wHQYDVR0OBBYEFOtVzO6eutPf
g85CvFu8q7fA5EzxMB8GA1UdIwQYMBaAFOtVzO6eutPfg85CvFu8q7fA5EzxMAwG
A1UdEwQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAFC+aeqkhe0YugcIP4cVCpPI
6xcfG0IZ82I94DhejVmsio2zP3kekR7ekzY4z28d6Kz03X0j+ll71xxZKZljnl3K
4C7oiBzj8ypCykXj03JDWNQ3sZNycfF/53OOk5Gu18qQ77TjqDpOGKU0xL45SmoB
oH/X2e9ccygMKOAAKqjT4ZVxksShC+tE+ij8ocBRfkMnJw0mfNAPlSjYw/Ho6WLJ
qlz0KyY5LkVHJbznXbN6S1K2FujJdyf7CkxMSb2bilDk8YvtXp8wFZg2R7Phi+dt
gbEx47N2hM0tLF0yPjAyBa+JKC2rG+/AS5ZfcTjYG3o+M84Y6l6QL3AXacn5L0k=
-----END CERTIFICATE-----

複製程式碼

localhost-privkey.pem

-----BEGIN PRIVATE KEY-----
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDUYnHsXrDbU241
Z8SshFmpRaL2VBGEPxUYwKyQJ3WhAUlZbK00Ra6d5xFDcejuhFDOEq3zkwnxFuj4
vWKh/MFZPPxtwQjhvfWrOhiB2m5Gwu/VIjXQ4QEC69dPoIGl5fkGMbxhL5P0G//S
0RXNxKrq1TZinRD1VFKYHEfHS/UpzoLi6ErigNlPuQGC5pV9JVBtkt+KPh/gpOnQ
vCCveLhTacjT73eMX/sRBEQZu0OSFS42dA+NJbhdYO0vSyJbU/AU2HmHglhqDYn0
HHEv+an8Litwu4GtvZXbiSjZg84Yiggk3pVPdvgjmIrIhLIGCuH2xZxh24hExbvO
yCbAAQ9pAgMBAAECggEANdwxX+wzlat2y3xhWA7IXjEWvrlbJ9qHkxtpp7UaE5ar
702sLxFs0waCTkRY+gP8KgZ6rsafQtC4jDwbA1GCBwt8SKkng3gVETNOe7/VL4TI
JZjjZPFqvD7q/3qI5nWHzZZXW54hO0rOebwkd2ZkoeEoRaHnZw/XUlP5sAUHS25p
LcmLcsY3j4vNG3px2K6yucfQwjidV0wSkh+xx5BXTwN65E3uwhBlu8DuV1G/Rv/H
/jXtX3FtF5tqGI/n4RtXGFmfv/ObbdICSxKFoprfv+fQaCk8NDm0uUadPHEOg5Yt
nTu6+52PEneo1dKR++1YVjHQGXqeW3xyuxuJEj6xwQKBgQD36qqkJ4Fo8+HhA1Ia
mKjt53ZItgRb1ecCIckWKG321ohCsVQB4GcNoN75dLt5uHtulcV4RjjK+mUJvQpD
iMnKdXcoJNzfdz0zqqeMH6grCs5FRqkj7UNhqtuSm9z/sQNjpcnE8gUOoA1hGQlA
JYIDcpTv0FkJGF72pyA+Q6Z05QKBgQDbTzIcWAW/wAg42K24hLtM4/hSaGHxovbt
7x1e0ymUVfXja3tvgrR6ptEI9jP+8tCJn9n4+TBifwKEMZdOU1JicFvVNjvRCEtg
INdm/Le8gzfYnCcfv0b9P1of9ZdbjHuYbe6sJZ2dm1h3auqojO/R1M7MT0vJkaYy
ZpbG8O2sNQKBgQCHCkoc1HAHLSESoe5tEk6iF/w0KwFAzMjiPmj8KtWLKNxcB5+M
ziEUKVaLZuxfpv+FAwvnMcjpt26l2VTn6HCSWV2ofjvZdWfe5swQ5YWCvIYS8iRb
r3eOkbS8rS26ET+ZXcsD/hiHGONwymRhjoy9OAKshj6ZV68Sh4JmqA7ZiQKBgFnQ
3VzuT6xwIO3nD356HZsn4hMd3L7xVt+rBgRHxseRTNqOskbA6NkyaHmbG0BWgUFb
zhFBPKeaDJXHGYhiZ2MZUQLI60Z4dyYvTQhIh5cUxlJX4U4HMDOXNnnQQuSjbBrJ
Ku4lmZ9qd5iwmNnennj5Bph2ATvUApSxnx5qnWvhAoGAPKLPKCuVoJS6FUZXl4ef
Pm0JYq+vl301wFAs9+4sf9kB3gA+xRFt8XHOmhaCgTDTea/EvSCR4FDO1BZgKpre
iyeQ5YdPYJ7sVq0TgQLCMzyqopG2f2/mmnJ/jzZhOp2C+gtqO0sKko6MZZTBEs3G
M94dSazKORHbqv9K6v49nls=
-----END PRIVATE KEY-----

複製程式碼

index.js

const http2 = require('http2');
const fs = require('fs');

const server = http2.createSecureServer({
  key: fs.readFileSync('localhost-privkey.pem'),
  cert: fs.readFileSync('localhost-cert.pem')
});
server.on('error', (err) => console.error(err));
server.on('socketError', (err) => console.error(err));

server.on('request', (request, response) => {
    console.log('./images'+request.url);
    
    //格式必須為 binary 否則會出錯'./images'+request.url
    var content =  fs.readFileSync('./images'+request.url,"binary");   
    response.writeHead(200, {
        "Content-Type": "image/png",
        "expires":"Tue, 09 Oct 2018 11:27:20 GMT",
        "Access-Control-Allow-Origin": "http://localhost:8080"
    });
    response.write(content,"binary"); //格式必須為 binary,否則會出錯
    response.end();
});

server.listen(8443);
複製程式碼

index.html

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title>HTTP/2測試</title>
        <script src="./http.js"></script>
    </head>
    <body>
123
<div id="imgContainer"></div>
    </body>
    <script>
        for(var i=1; i<16; i++){
            // GET('https://localhost:8443/'+i+'.png',function(response){
            //     drawImg(response);
            // });

            // GET('http://localhost:8080/http2test/images/'+i+'.png',function(response){
            //     drawImg(response);
            // });

            // var myImage = new Image();
            // myImage.src = 'https://localhost:8443/'+i+'.png';
            // document.body.appendChild(myImage);

            var myImage = new Image();
            myImage.src = 'http://localhost:8080/http2test/images/'+i+'.png';
            document.body.appendChild(myImage);

            
        }


        function drawImg(data){
            var img = new Image();
            img.src = window.URL.createObjectURL(data);
            img.onload = function(){
                window.URL.revokeObjectURL(img.src);
            }
            document.body.appendChild(img);
        }
        
    </script>
</html>
複製程式碼

http.js

/**
 * 發起非同步請求的庫
 */

 function GET(url,callback){
    var xhr = new XMLHttpRequest();
    xhr.open('GET',url,true);
    xhr.responseType = 'blob';
    xhr.onload = function(){
        if(xhr.readyState === 4 && xhr.status >= 200 && xhr.status <300 && xhr.response){
            callback(xhr.response);
        }
    }
    xhr.send();
 }
複製程式碼

rename.js(用於快速生成大量不同名的相同檔案)

const fs = require('fs');


var path = './images';
var dirs = fs.readdirSync(path);
var n = 0;
for(let i in dirs){
    var filename = dirs[i].split('.');
    var type = filename[filename.length-1];
    if(type=='png'){
        fs.renameSync(path+'/'+dirs[i],path+'/'+n+'.png');
        n +=1;
    }else{
        fs.unlinkSync(path+'/'+dirs[i]);
    }
    console.log('處理完成:'+dirs[i]);
}
複製程式碼

完整程式碼地址: 可以直接跑起來的http/2與http/1.1的對比測試

測試結果:

因為本地載入實在太快,為儘量模擬生產環境網路情況,以下測試都是使用Chrome的fast3G模式。

型別 請求方式 大(237kb)小(2kb)檔案 檔案數 載入時間
HTTP/2 瀏覽器載入 40 2.81s
HTTP/1.1 瀏覽器載入 40 5.89s
HTTP/2 AJAX 40 2.49s
HTTP/1.1 AJAX 40 5.82s
HTTP/2 瀏覽器載入 40 52.59s
HTTP/1.1 瀏覽器載入 40 52.57s
HTTP/2 AJAX 40 52.66s
HTTP/1.1 AJAX 40 52.58s

由此可見: _HTTP/2的優勢主要體現在大量小圖片的載入上,對於少量大圖片的載入並不佔太大優勢,並且,網路條件如果非常好的時候,由於載入非常快,HTTP/2也不能體現出優勢。

HTTP/2在WebGIS等地圖應用上的設想:

WebGIS以瓦片技術得以普及,瓦片即請求和檔案,每載入一個瓦片都需要請求一次伺服器上的圖片或其它格式的檔案。地圖瓦片大多為40kb左右的png檔案,且一次載入數量都在20左右。由於http1.1本身的限制,瀏覽器只能同時載入6個檔案(不同的瀏覽器策略額細微的差別),當地圖瀏覽視窗移動時,前端會同時傳送大量AJAX請求來載入瓦片。 此時瀏覽器就會受到阻塞,需要先等待佇列前面的檔案載入完成才能載入後面的檔案。

HTTP/2的併發特性正好能解決此問題,也正好能對應這種場景。

高德地圖部分使用了HTTP/2, 谷歌地圖全部使用了HTTP/2 百度地圖還沒有使用HTTP/2 mapbox沒有使用HTTP/2

目前唯一的問題是需要統計ie11的市場佔用率,若佔用率還是太高切換之後處理起來是個麻煩事。

參考:

Node.js中HTTP/2的使用:nodejs.org/api/http2.h…

PHP中HTTP/2深入分析:www.phpchina.com/article-402…

相關文章