如何在Nginx中啟用HTTP 3.0/QUIC支持
在 Nginx 中支持 HTTP3.0 / QUIC
HTTP 3.0,也稱作 HTTP over QUIC。核心是 QUIC (讀音quick)協(xié)議,由 Google 在 2015 年提出的 SPDY v3 演化而來的新協(xié)議,傳統(tǒng)的 HTTP 協(xié)議是基于傳輸層 TCP 的協(xié)議,而 QUIC 是基于傳輸層 UDP 上的協(xié)議,可以定義成:HTTP3.0 基于 UDP 的安全可靠的 HTTP2.0 協(xié)議,主要有以下特性:
圖片來自Nginx官博
- 基于 UDP 減少了 TCP 三次握手及 TLS 握手時間
- 解決多路復(fù)用丟包時的線頭阻塞問題
- 優(yōu)化重傳策略
- 流量控制
- 連接遷移
本文主要講解一下如何在 Nginx 中開啟 HTTP3.0 的支持。
方案選擇
對于 HTTP3.0,由于整個協(xié)議還是處于草案階段,目前來說沒有一個完整的標(biāo)準(zhǔn),所以各大瀏覽器廠商還只是在開發(fā)者版本中才會支持,例如 Chrome的金絲雀版本 Chrome Canary[1],并且各個服務(wù)器廠商也是在持續(xù)跟進(jìn)中,對于 Nginx 來說,支持 HTTP3.0 目前有兩種方案可以選擇:
- 基于 Cloudflare 的分支版本 Nginx:對于 HTTP3.0/QUIC,Cloudflare 始終走在了前列,借助于自家維護(hù)的開源項目 quic[2],從 Nginx 中拉出了一個分支來開發(fā),并編譯出了對 HTTP 3.0 支持的 Nginx 服務(wù)器。
- Nginx 官方 Nginx-quic 項目:今年 6 月1 0 日,Nginx 官博[3]發(fā)布公告稱已經(jīng)在研發(fā)支持 HTTP3.0/QUIC 協(xié)議的工作,目前項目維護(hù)在 nginx-quic[4],該項目和基于 Cloudflare 基于 Nginx 的分支并無關(guān)系,算是相對于正統(tǒng)的方案。
基于此,本文將會以部署 nginx-quic 的方式來讓 Nginx 支持 HTTP3.0/QUIC。
改造過程
我們最終的目的是得到 nginx-quic 版本的 nginx 可執(zhí)行文件,需要經(jīng)過一系列的安裝和編譯,期間可能會遇到很多問題,如果各位讀者不想實際操作,可以直接用我編譯好的版本 nginx-quic.linux-x86_64.zip 傳送門[5]。
準(zhǔn)備工作:
以 centos7 為例,下載 nginx-quic 源碼傳送門[6],下載完成之后,需要進(jìn)行編譯安裝,由于 nginx-quic 依賴 boringSSL,所以還需下載 boringSSL 源碼傳送門[7],然后同樣需要編譯安裝 boringSSL,執(zhí)行這些操作之前,需要在 linux 上安裝一些前置模塊,通過 yum 來安裝,執(zhí)行以下命令:
- 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 是谷歌創(chuàng)建的 OpenSSL 分支,用于支持 TLS1.3的 UDP 協(xié)議 0-RTT 數(shù)據(jù)傳輸?shù)募用芩惴ǎ梢岳斫獬?TLS 1.3 是標(biāo)準(zhǔn)協(xié)議,BoringSSL 是實現(xiàn)工具),BoringSSL 的一些特性會在合適的時機(jī)同步給 OpenSSl。
編譯安裝 boringSSL:
- cd boringssl-master/
- mkdir build
- cd build
- cmake ../
- make
執(zhí)行之后,可以在 build/crypto,和 build/ssl下獲得對應(yīng)的文件,如下圖:
注意編譯安裝 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
執(zhí)行命令之后,會在/root/nginx目錄下生成對應(yīng)的 nginx 可執(zhí)行文件,如下圖:
其中,配置文件在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 版本,并且當(dāng)瀏覽器不支持 http3 時,可以選擇 http2。另外,add_header Alt-Svc添加這個返回頭不可缺少。
- Alt-Svc 全稱為 “Alternative-Service”,直譯為“備選服務(wù)”。該頭部列舉了當(dāng)前站點備選的訪問方式列表,讓服務(wù)器可以告訴客戶端 “看,我在這個主機(jī)的這個端口用這個協(xié)議提供相同的服務(wù)”。一般用于在提供 “QUIC” 等新興協(xié)議支持的同時,實現(xiàn)向下兼容。參考 MDN[8]。
驗證 HTTP3 生效:
由于目前瀏覽器對HTTP3.0/QUIC的支持性有限,可以通過 http3check.net/[9] 來驗證站點啟用HTTP3是否成功,以我的站點為例:
坑點總結(jié)
整個過程看似很簡單,但是真正配置過程中遇到了不少坑,前前后后加上搜索問題花了一天半的時間才真正解決,把這些問題記錄下來,分享給大家。
開啟 UDP 的 443 端口:
由于 quic 協(xié)議使用的是 UDP 的 443 端口,這個端口對于 centos7 來說是默認(rèn)關(guān)閉的,可以采用下面命令開啟:
- firewall-cmd --zone=public --add-port=443/udp --permanent
如果項目托管在阿里云上,需要更新 ECS 的安全組策略來對外開啟對應(yīng)的協(xié)議和端口,如下圖:
TLS 版本向下兼容:
由于使用了 TLS 1.3,所以會修改對應(yīng)加密算法,但是對于一些瀏覽器而言還不支持這么高的版本,尤其是對于蘋果的 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 參數(shù)去掉。
reuseport 只需配置一次:
假如有多個域名需要開啟 http3,則 reuseport 建議只在根域名上配置,如果一個配置文件中出現(xiàn)多個 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;
- }
編譯安裝時的性能問題:
如果編譯安裝時報類似下面的錯誤,可能是主機(jī)的內(nèi)容不足,需要關(guān)閉一些運行的程序來往下進(jìn)行。
- ...
- c++: internal compiler error: Killed (program cc1plus)
快開啟你的 HTTP3 之旅吧!