天天看點

在Nginx中支援HTTP3.0/QUIC

在Nginx中支援HTTP3.0/QUIC

HTTP3.0,也稱作HTTP over QUIC。核心是QUIC(讀音quick)協定,由Google在2015年提出的SPDY v3演化而來的新協定,傳統的HTTP協定是基于傳輸層TCP的協定,而QUIC是基于傳輸層UDP上的協定,可以定義成:HTTP3.0基于UDP的安全可靠的HTTP2.0協定,主要有以下特性:

在Nginx中支援HTTP3.0/QUIC
圖檔來自Nginx官博

  • 基于UDP減少了TCP三向交握及TLS握手時間
  • 解決多路複用丢包時的線頭阻塞問題
  • 優化重傳政策
  • 流量控制
  • 連接配接遷移

本文主要講解一下如何在Nginx中開啟HTTP3.0的支援。

方案選擇

對于HTTP3.0,由于整個協定還是處于草案階段,目前來說沒有一個完整的标準,是以各大浏覽器廠商還隻是在開發者版本中才會支援,例如Chrome的金絲雀版本Chrome Canary[1],并且各個伺服器廠商也是在持續跟進中,對于Nginx來說,支援HTTP3.0目前有兩種方案可以選擇:

  • 基于Cloudflare的分支版本Nginx:對于HTTP3.0/QUIC,Cloudflare始終走在了前列,借助于自家維護的開源項目 quic [2],從Nginx中拉出了一個分支來開發,并編譯出了對HTTP3.0支援的Nginx伺服器。
  • Nginx官方Nginx-quic項目:今年6月10日,Nginx 官博 [3]釋出公告稱已經在研發支援HTTP3.0/QUIC協定的工作,目前項目維護在 nginx-quic [4],該項目和基于Cloudflare基于Nginx的分支并無關系,算是相對于正統的方案。

基于此,本文将會以部署nginx-quic的方式來讓Nginx支援HTTP3.0/QUIC。

改造過程

我們最終的目的是得到nginx-quic版本的nginx可執行檔案,需要經過一系列的安裝和編譯,期間可能會遇到很多問題,如果各位讀者不想實際操作,可以直接用我編譯好的版本nginx-quic.linux-x86_64.zip 傳送門[5]。

準備工作:

以centos7為例,下載下傳nginx-quic源碼傳送門[6],下載下傳完成之後,需要進行編譯安裝,由于nginx-quic依賴boringSSL,是以還需下載下傳boringSSL源碼傳送門[7],然後同樣需要編譯安裝boringSSL,執行這些操作之前,需要在linux上安裝一些前置子產品,通過yum來安裝,執行以下指令:

sudo yum install build-essential mercurial psmisc lsb-release cmake golang libunwind-dev git libpcre3-dev zlib1g-dev      

什麼是boringSSL:

對于Nginx來說,在編譯時需要配置對于的SSL庫,不管是HTTP3.0還是HTTP2.0,始終都要基于HTTPS,而加密算法這塊主要有OpenSSL來提供,而BoringSSL是谷歌建立的OpenSSL分支,用于支援TLS1.3的UDP協定0-RTT資料傳輸的加密算法(可以了解成TLS1.3是标準協定,BoringSSL是實作工具),BoringSSL的一些特性會在合适的時機同步給OpenSSl。

編譯安裝boringSSL:

cd boringssl-master/
mkdir build
cd build
cmake ../
make      

執行之後,可以在build/crypto,和build/ssl下獲得對應的檔案,如下圖:

在Nginx中支援HTTP3.0/QUIC
在Nginx中支援HTTP3.0/QUIC

注意編譯安裝boringSSL需要使用cmake3以上的版本。

編譯安裝nginx-quic:

cd nginx-quic/
./auto/configure --prefix=/root/nginx --with-http_ssl_module --with-http_v2_module --with-http_v3_module --with-cc-opt="-I../boringssl-master/include" --with-ld-opt="-L../boringssl-master/build/ssl -L../boringssl-master/build/crypto"
make 
make install      

執行指令之後,會在/root/nginx目錄下生成對應的nginx可執行檔案,如下圖:

在Nginx中支援HTTP3.0/QUIC

其中,配置檔案在conf/下,nginx指令在sbin/目錄下。

修改配置檔案,啟動nginx:

vi /root/nginx/conf/nginx.conf      

添加http3配置:

server {
    listen 443 ssl http2;              # TCP listener for HTTP/2
    listen 443 http3 reuseport;  # UDP listener for QUIC+HTTP/3

    ssl_protocols       TLSv1.3; # QUIC requires TLS 1.3
    ssl_certificate     ssl/www.example.com.crt;
    ssl_certificate_key ssl/www.example.com.key;

    add_header Alt-Svc 'quic=":443"; h3-27=":443";h3-25=":443"; h3-T050=":443"; h3-Q050=":443";h3-Q049=":443";h3-Q048=":443"; h3-Q046=":443"; h3-Q043=":443"'; # Advertise that QUIC is available
}      

其中,要求使用TLSv1.3版本,并且當浏覽器不支援http3時,可以選擇http2。另外,add_header Alt-Svc添加這個傳回頭不可缺少。

  • Alt-Svc 全稱為“Alternative-Service”,直譯為“備選服務”。該頭部列舉了目前站點備選的通路方式清單,讓伺服器可以告訴用戶端 “看,我在這個主機的這個端口用這個協定提供相同的服務”。一般用于在提供 “QUIC” 等新興協定支援的同時,實作向下相容。參考 MDN [8]。

驗證HTTP3生效:

由于目前浏覽器對HTTP3.0/QUIC的支援性有限,可以通過http3check.net/[9]來驗證站點啟用HTTP3是否成功,以我的站點為例:

在Nginx中支援HTTP3.0/QUIC

坑點總結

整個過程看似很簡單,但是真正配置過程中遇到了不少坑,前前後後加上搜尋問題花了一天半的時間才真正解決,把這些問題記錄下來,分享給大家。

開啟UDP的443端口:

由于quic協定使用的是UDP的443端口,這個端口對于centos7來說是預設關閉的,可以采用下面指令開啟:

firewall-cmd --zone=public --add-port=443/udp --permanent      

如果項目托管在阿裡雲上,需要更新ECS的安全組政策來對外開啟對應的協定和端口,如下圖:

在Nginx中支援HTTP3.0/QUIC

TLS版本向下相容:

由于使用了TLS 1.3,是以會修改對應加密算法,但是對于一些浏覽器而言還不支援這麼高的版本,尤其是對于蘋果的Safari,是以,在配置nginx配置檔案時,要多配置幾個版本向下相容,代碼如下:

ssl_protocols       TLSv1 TLSv1.1 TLSv1.2 TLSv1.3;      

-Werror錯誤問題:

在編譯nginx-quic時,有時會遇到如下錯誤:

cc -c -pipe  -O -W -Wall -Wpointer-arith -Wno-unused-parameter -Werror -g -I../boringssl-master/include -I src/core -I src/event -I src/event/modules -I src/os/unix -I objs \
 -o objs/src/os/unix/ngx_linux_sendfile_chain.o \
 src/os/unix/ngx_linux_sendfile_chain.c
cc -c -pipe  -O -W -Wall -Wpointer-arith -Wno-unused-parameter -Werror -g -I../boringssl-master/include -I src/core -I src/event -I src/event/modules -I src/os/unix -I objs \
 -o objs/src/event/ngx_event_openssl.o \
 src/event/ngx_event_openssl.c
cc -c -pipe  -O -W -Wall -Wpointer-arith -Wno-unused-parameter -Werror -g -I../boringssl-master/include -I src/core -I src/event -I src/event/modules -I src/os/unix -I objs \
 -o objs/src/event/ngx_event_openssl_stapling.o \
 src/event/ngx_event_openssl_stapling.c
cc -c -pipe  -O -W -Wall -Wpointer-arith -Wno-unused-parameter -Werror -g -I../boringssl-master/include -I src/core -I src/event -I src/event/modules -I src/os/unix -I objs \
 -o objs/src/event/ngx_event_quic.o \
 src/event/ngx_event_quic.c
cc -c -pipe  -O -W -Wall -Wpointer-arith -Wno-unused-parameter -Werror -g -I../boringssl-master/include -I src/core -I src/event -I src/event/modules -I src/os/unix -I objs \
 -o objs/src/event/ngx_event_quic_transport.o \
 src/event/ngx_event_quic_transport.c
src/event/ngx_event_quic_transport.c: In function ‘ngx_quic_create_stream’:
src/event/ngx_event_quic_transport.c:54:9: error: comparison is always true due to limited range of data type [-Werror=type-limits]
      : ((uint32_t) value) <= 16383 ? 2                                        \
         ^
src/event/ngx_event_quic_transport.c:1299:15: note: in expansion of macro ‘ngx_quic_varint_len’
         len = ngx_quic_varint_len(sf->type);
               ^
cc1: all warnings being treated as errors
make[1]: *** [objs/src/event/ngx_event_quic_transport.o] Error 1
make[1]: Leaving directory `/root/nginx-quic'
make: *** [build] Error 2
[root@iz2zehmi1ztqtx8tg6ca7gz nginx-quic]#      

解決辦法是:

cd nginx-quic\objs

vi Makefile      

找到 CFLAGS = -pipe -O -W -Wall -Wpointer-arith -Wno-unused-parameter -Werror -g -I../boringssl-master/include将-Werror參數去掉。

reuseport隻需配置一次:

    server {
        
        listen 443 ssl http2;              # TCP listener for HTTP/2
        listen 443 http3 reuseport;  # UDP listener for QUIC+HTTP/3

        server_name  www.nihaoshijie.com.cn default_server;

    }

    server {

        listen 443 ssl http2;        # TCP listener for HTTP/2
        listen 443 http3;  # UDP listener for QUIC+HTTP/3

        server_name  app.nihaoshijie.com.cn;

    }      

編譯安裝時的性能問題:

...
c++: internal compiler error: Killed (program cc1plus)      

繼續閱讀