天天看點

Fundebug網站更新HTTP/2,真的變快了!

作為新一代的HTTP協定,HTTP/2可以提高網站性能,優化使用者體驗, Fundebug

也是時候更新HTTP/2了,雖然已經有點晚了。

更新HTTP/2是一件很簡單的事情,改1行Nginx配置就好了,但是,工程師隻知道How是遠遠不夠的,還需要了解Why,這就要求我們需要足夠的事先調研(1. 什麼是HTTP/2?)以及事後分析(4. 更新HTTP/2真的提高性能了嗎?)。

1. 什麼是HTTP/2?

HTTP/2是新一代的HTTP協定,于2015正式釋出。

與其他衆多Web技術标準一樣,推動HTTP/2标準的依然是Google。釋出Chrome的時候Google說過要推動Web技術的發展,然後它真的做到了。(

JavaScript深入淺出第5課:Chrome是如何成功的?

根據

W3Techs

的統計,截止2019年10月26日,全世界41.3%的網站已經使用了HTTP/2。

Can I use

,絕大多數浏覽器都支援了HTTP/2:

Fundebug網站更新HTTP/2,真的變快了!

HTTP/2主要有以下幾個特性:

  • HTTP/2為二進制協定
Fundebug網站更新HTTP/2,真的變快了!

圖檔來源:

Valentin V. Bartenev

由上圖可知,HTTP/1.1傳輸的是文本資料,而HTTP/2傳輸的是二進制資料,提高了資料傳輸效率。

  • HTTP/2支援TCP連接配接多路複用
Fundebug網站更新HTTP/2,真的變快了!
Factory.hr

由上圖可知,HTTP 1.1需要為不同的HTTP請求建立單獨的TCP連接配接,而HTTP/2的多個HTTP請求可以複用同一個TCP連接配接。

要知道,建立TCP連接配接時需要3次握手,再加上TLS的4次握手,加起來就是7次握手,如果可以複用TCP連接配接的話,則可以減少這些多餘的開銷。

  • HTTP/2會壓縮請求Header
Fundebug網站更新HTTP/2,真的變快了!
運維實談

如上圖所示,第2個請求的Header隻有:path不一樣,是以壓縮空間非常可觀。

Headers壓縮的算法HPACK本身似乎很複雜(其實也不難),但是算法思想其實非常簡單的,假設我們在浏覽器發起100個請求,它們的user-agent是不會變的,那我們為什麼需要重複傳輸這個長長的字元串呢?用dictionary記錄一次不就行了!

  • HTTP/2支援伺服器推送(Server Push)
Fundebug網站更新HTTP/2,真的變快了!
lujjjh

由上圖可知,當客服端向服務端請求HTML時,Server Push服務端可以提前傳回HTML所依賴的css、js等資源,這樣可以節省解析HTML以及請求資源的時間,進而縮短頁面的加載時間。

2. 如何更新HTTP/2?

我們使用了Nginx作為前端頁面與後端接口的反向代理伺服器(Reverse Proxy),隻需要修改一下Nginx配置檔案就可以更新HTTP/2了,非常簡單。

注意,在 Nginx 上 開啟 HTTP/2 需要 Nginx 1.9.5 以上版本(包括1.9.5),并且需要 OpenSSL 1.0.2 以上版本(包括1.0.2)。使用

nginx -V

指令可以檢視Nginx的版本資訊:

nginx -V
nginx version: nginx/1.12.1
built by gcc 6.3.0 20170516 (Debian 6.3.0-18)
built with OpenSSL 1.1.0f  25 May 2017
TLS SNI support enabled
configure arguments: --prefix=/etc/nginx --sbin3-path=/usr/sbin/nginx --modules-path=/usr/lib/nginx/modules --conf-path=/etc/nginx/nginx.conf --error-log-path=/var/log/nginx/error.log --http-log-path=/var/log/nginx/access.log --pid-path=/var/run/nginx.pid --lock-path=/var/run/nginx.lock --http-client-body-temp-path=/var/cache/nginx/client_temp --http-proxy-temp-path=/var/cache/nginx/proxy_temp --http-fastcgi-temp-path=/var/cache/nginx/fastcgi_temp --http-uwsgi-temp-path=/var/cache/nginx/uwsgi_temp --http-scgi-temp-path=/var/cache/nginx/scgi_temp --user=nginx --group=nginx --with-compat --with-file-aio --with-threads --with-http_addition_module --with-http_auth_request_module --with-http_dav_module --with-http_flv_module --with-http_gunzip_module --with-http_gzip_static_module --with-http_mp4_module --with-http_random_index_module --with-http_realip_module --with-http_secure_link_module --with-http_slice_module --with-http_ssl_module --with-http_stub_status_module --with-http_sub_module --with-http_v2_module --with-mail --with-mail_ssl_module --with-stream --with-stream_realip_module --with-stream_ssl_module --with-stream_ssl_preread_module --with-cc-opt='-g -O2 -fdebug-prefix-map=/data/builder/debuild/nginx-1.12.1/debian/debuild-base/nginx-1.12.1=. -specs=/usr/share/dpkg/no-pie-compile.specs -fstack-protector-strong -Wformat -Werror=format-security -Wp,-D_FORTIFY_SOURCE=2 -fPIC' --with-ld-opt='-specs=/usr/share/dpkg/no-pie-link.specs -Wl,-z,relro -Wl,-z,now -Wl,--as-needed -pie'           

可知,我們使用的Nginx版本為1.12.1,OpenSSL版本為1.1.0f,符合要求。

還有一點,雖然HTTP/2标準并沒有要求加密,但是所有浏覽器都要求HTTP/2必須加密,這樣的話,隻有HTTPS才能更新HTTP/2。

如果你還沒用過HTTPS的話,不妨看看我的部落格:

教你快速撸一個免費HTTPS證書

,其實也很簡單。

一切前提沒問題的話(Nginx>=1.9.5,OpenSSL>=1.0.2,HTTPS),隻需要修改1行配置,在listen指令後面添加http2:

server
{
    listen 443 ssl http2;
    server_name www.fundebug.com;
}           

重新開機Nginx,更新HTTP/2就成功了,可以使用curl指令檢查:

curl -sI https://www.fundebug.com
HTTP/2 200
server: nginx/1.12.1
date: Mon, 07 Oct 2019 00:12:53 GMT
content-type: text/html; charset=UTF-8
content-length: 4892
x-powered-by: Express
accept-ranges: bytes
cache-control: public, max-age=0
last-modified: Sun, 06 Oct 2019 23:07:25 GMT
etag: W/"131c-16da353dbc8"
vary: Accept-Encoding
strict-transport-security: max-age=15768001           

3. HTTP/2導緻Safari浏覽器OPTIONS請求失敗

更新HTTP/2之後,使用Safari的使用者發現無法登陸Fundebug了:

Fundebug網站更新HTTP/2,真的變快了!

我們的前端異常監控插件捕獲了這個報錯:

Fundebug網站更新HTTP/2,真的變快了!

可知,是/api/members/login接口出錯了。

經過排查發現是OPTIONS請求失敗了:

curl -X OPTIONS https://api.fundebug.com/api/members/login -v
*   Trying 120.77.45.162...
* TCP_NODELAY set
* Connected to api.fundebug.com (120.77.45.162) port 443 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
* Cipher selection: ALL:!EXPORT:!EXPORT40:!EXPORT56:!aNULL:!LOW:!RC4:@STRENGTH
* successfully set certificate verify locations:
*   CAfile: /etc/ssl/cert.pem
  CApath: none
* TLSv1.2 (OUT), TLS handshake, Client hello (1):
* TLSv1.2 (IN), TLS handshake, Server hello (2):
* TLSv1.2 (IN), TLS handshake, Certificate (11):
* TLSv1.2 (IN), TLS handshake, Server key exchange (12):
* TLSv1.2 (IN), TLS handshake, Server finished (14):
* TLSv1.2 (OUT), TLS handshake, Client key exchange (16):
* TLSv1.2 (OUT), TLS change cipher, Client hello (1):
* TLSv1.2 (OUT), TLS handshake, Finished (20):
* TLSv1.2 (IN), TLS change cipher, Client hello (1):
* TLSv1.2 (IN), TLS handshake, Finished (20):
* SSL connection using TLSv1.2 / ECDHE-RSA-AES128-GCM-SHA256
* ALPN, server accepted to use h2
* Server certificate:
*  subject: CN=api.fundebug.com
*  start date: Sep 15 16:38:43 2019 GMT
*  expire date: Dec 14 16:38:43 2019 GMT
*  subjectAltName: host "api.fundebug.com" matched cert's "api.fundebug.com"
*  issuer: C=US; O=Let's Encrypt; CN=Let's Encrypt Authority X3
*  SSL certificate verify ok.
* Using HTTP2, server supports multi-use
* Connection state changed (HTTP/2 confirmed)
* Copying HTTP/2 data in stream buffer to connection buffer after upgrade: len=0
* Using Stream ID: 1 (easy handle 0x7fcfbb80ce00)
> OPTIONS /api/members/login HTTP/2
> Host: api.fundebug.com
> User-Agent: curl/7.54.0
> Accept: */*
>
* Connection state changed (MAX_CONCURRENT_STREAMS updated)!
* http2 error: Invalid HTTP header field was received: frame type: 1, stream: 1, name: [content-length], value: [0]
* HTTP/2 stream 1 was not closed cleanly: PROTOCOL_ERROR (err 1)
* Closing connection 0
* TLSv1.2 (OUT), TLS alert, Client hello (1):
curl: (92) HTTP/2 stream 1 was not closed cleanly: PROTOCOL_ERROR (err 1)           

根據curl的報錯資訊,可知是Header中content-length有問題:

* http2 error: Invalid HTTP header field was received: frame type: 1, stream: 1, name: [content-length], value: [0]           

将Nginx配置檔案中OPTIONS請求的Content-Length配置注釋掉,問題就解決了:

if ($request_method = "OPTIONS")
{
    add_header Access-Control-Allow-Origin *;
    add_header 'Access-Control-Max-Age' 86400;
    add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, OPTIONS, DELETE';
    add_header 'Access-Control-Allow-Headers' 'token, reqid, nid, host, x-real-ip, x-forwarded-ip, event-type, event-id, accept, content-type';
    # add_header 'Content-Length' 0; // 必須注釋,否則HTTP/2會報錯
    add_header 'Content-Type' 'text/plain, charset=utf-8';
    return 200;
}           

HTTP/2中對于Header有特殊處理,這應該是導緻出錯的根本原因,關于這一個問題,我會在下一篇部落格中詳細介紹。

4. 更新HTTP/2真的提高性能了嗎?

理論上來說,HTTP/2應該可以提高網站性能,但是實際情況是怎樣呢?HTTP/2真的可以提高性能了嗎?如果有的話,究竟提高了多少呢?

于是,我使用Chrome記錄了更新HTTP/2前後

Fundebug首頁

的加載時間,計算了5次加載的平均時間(機關為妙),如下表:

HTTP版本 DOMContentLoaded Load Finish
HTTP/1.1 1.572 4.342 5.138
HTTP/2 1.0004 4.102 4.288

可知,HTTP/2明顯提高了首頁加載時間,DOMContentLoaded、Load與Finish時間均有明顯提高。

一共也就改了2行Nginx配置,就可以提高頁面通路性能,多好啊!

參考

繼續閱讀