起航天空的http3探索

起航天空的http3探索


起航天空近日开始尝试实现http3+QUIC协议支持

什么是HTTP/3和QUIC

HTTP2.0 也是基于TCP协议的,tcp协议在处理包时是有严格顺序的

当其中一个数据包遇到问题,TCP连接需要等待找个包完成重传之后才能继续进行,虽然HTTP2.0通过多个stream,使得逻辑上一个tcp连接上的并行内容,进行多路数据的传输,然而这中间没有关联的数据,一前一后,前面stream2的帧没有收到,后面stream1的帧也会因此堵塞

于是google的 QUIC协议从TCP切换到UDP

自定义连接机制

一条tcp连接是由四元组标识的,分别是源ip、目的ip、源端口、目的端口,一旦一个元素发生变化时,就会断开重连,重新连接。在次进行三次握手,导致一定的延时

在TCP是没有办法的,但是基于UDP,就可以在QUIC自己的逻辑里面维护连接的机制,不再以四元组标识,而是以一个64位的随机数作为ID来标识,而且UDP是无连接的,所以当ip或者端口变化的时候,只要ID不变,就不需要重新建立连接

自定义重传机制

tcp为了保证可靠性,通过使用序号和应答机制,来解决顺序问题和丢包问题

任何一个序号的包发过去,都要在一定的时间内得到应答,否则一旦超时,就会重发这个序号的包,通过自适应重传算法进行重传(通过采样往返时间RTT不断调整)

但是,在TCP里面超时的采样存在不准确的问题。例如发送一个包,序号100,发现没有返回,于是在发送一个100,过一阵返回ACK101,客户端收到了,但是往返的时间是多少,没法计算。是ACK到达的时候减去第一还是第二。

QUIC也有个序列号,是递增的,任何一个序号的包只发送一次,下次就要加1,那样就计算可以准确了

但是有一个问题,就是怎么知道包100和包101发送的是同样的内容呢?quic定义了一个offset概念。QUIC既然是面向连接的,也就像TCP一样,是一个数据流,发送的数据在这个数据流里面有个偏移量offset,可以通过offset查看数据发送到了那里,这样只有这个offset的包没有来,就要重发。如果来了,按照offset拼接,还是能够拼成一个流。

无阻塞的多路复用

有了自定义的连接和重传机制,就可以解决上面HTTP2.0的多路复用问题

同HTTP2.0一样,同一条 QUIC连接上可以创建多个stream,来发送多个HTTP请求,但是,QUIC是基于UDP的,一个连接上的多个stream之间没有依赖。这样,假如stream2丢了一个UDP包,后面跟着stream3的一个UDP包,虽然stream2的那个包需要重新传,但是stream3的包无需等待,就可以发给用户。

自定义流量控制

TCP的流量控制是通过滑动窗口协议。QUIC的流量控制也是通过window_update,来告诉对端它可以接受的字节数。但是QUIC的窗口是适应自己的多路复用机制的,不但在一个连接上控制窗口,还在一个连接中的每个steam控制窗口。

在TCP协议中,接收端的窗口的起始点是下一个要接收并且ACK的包,即便后来的包都到了,放在缓存里面,窗口也不能右移,因为TCP的ACK机制是基于序列号的累计应答,一旦ACK了一个序列号,就说明前面的都到了,所以是要前面的没到,后面的到了也不能ACK,就会导致后面的到了,也有可能超时重传,浪费带宽

QUIC的ACK是基于offset的,每个offset的包来了,进了缓存,就可以应答,应答后就不会重发,中间的空档会等待到来或者重发,而窗口的起始位置为当前收到的最大offset,从这个offset到当前的stream所能容纳的最大缓存,是真正的窗口的大小,显然,那样更加准确。

关于Nginx Quic技术预览版

Nginx官方推出了一个预览版nginx-quic以支持全新的QUIC+HTTP/3传输协议。nginx-quic基于IETF QUIC草案,在Nginx开发分支中维护,与稳定分支和干线分支隔离。

nginx-quic

quic-test

经过几个月紧张开发的,现在发布测试预览版,发布公测,以对其互操作性测试,问题反馈和社区代码贡献。同期nginx还发布了一个演示站(quic.nginx.org)点进行功能演示。

部署

为了这次探索,我准备了一个名为nginx-enhance-module的存储库来收录一些常用的模块和nginx-quic,我们使用如下命令来初始化这个模块:

sudo apt install openssl libssl-dev libxml2 libxml2-dev libxslt1.1 libxslt1-dev libpcre3-dev zlib1g-dev -y
sudo apt install mercurial
sudo apt install python3-pip
git clone https://github.com/jack9603301/nginx-enhance-module
cd nginx-enhance-module
sudo python3 -m pip install -r requirements.txt
./download_dependency.py

我们可以利用以上命令来获取nginx-enhance-module收录的模块。然后开始准备编译基本依赖:

cd depends
git clone https://github.com/ivmai/libatomic_ops.git
cd libatomic_ops
./autogen.sh
./configure --prefix=/usr
make
sudo make install
cd ..
cd pcre
./configure --prefix=/usr/local
make
sudo make install
cd ../LuaJIT
make
sudo make install PREFIX=/usr/local
echo "/usr/local/lib" > /etc/ld.so.conf.d/usr_local_luajit_lib.conf
sudo ldconfig
export C_INCLUDE_PATH="/usr/local/include/luajit-2.0/"
export CXX_INCLUDE_PATH="/usr/local/include/luajit-2.0/"
export CPLUS_INCLUDE_PATH="/usr/local/include/luajit-2.0/"
cd ../../modules/boringssl
mkdir build
cd build
cmake ..
make
cd ../..
cp boringssl/build/crypto/libcrypto.a boringssl/build/ssl/libssl.a boringssl/.openssl/lib
cd boringssl/.openssl
ln -s ../include .
cd ../..
cd ../depends/nginx-quic
./auto/configure  --prefix=/usr/local/nginx --user=www --group=www --error-log-path=/www/logs/nginx-error.log --conf-path=/www/services/nginx/nginx.conf --with-http_stub_status_module --with-stream --with-stream_ssl_module --with-http_ssl_module --with-http_dav_module --add-module=../../modules/nginx-dav-ext-module --with-stream --with-stream_ssl_module --with-http_v2_module --with-stream --with-stream_ssl_module --with-stream --with-stream_ssl_module --with-stream --with-stream_ssl_module --with-http_realip_module --with-stream --with-stream_ssl_module --with-pcre --with-libatomic --with-threads --with-file-aio --with-poll_module --with-select_module --with-stream --with-stream_ssl_module --with-stream --with-stream_ssl_module --add-module=../../modules/ngx_devel_kit --add-module=../../modules/naxsi/naxsi_src --add-module=../../modules/nginx-module-vts  --add-module=../../modules/ngx_brotli  --add-module=../../modules/nginx-sticky-module-ng --add-module=../../modules/headers-more-nginx-module --with-cc-opt='-I/root/nginx-enhance-module/modules/boringssl/include -O0 -fno-common -fno-omit-frame-pointer -DNGX_QUIC_DRAFT_VERSION=29' --with-ld-opt='-L/root/nginx-enhance-module/modules/boringssl/build/ssl -L/root/nginx-enhance-module/modules/boringssl/build/crypto' --with-http_v3_module --with-http_quic_module --with-stream_quic_module --build=$(hg tip | head -n 1 | awk '{ print $2 }')
make
sudo make install

配置

接下来就需要修改配置了,如:

upstream qhjack-https {
    server 66.42.99.20:443 max_fails=10 fail_timeout=120s;
    server 45.32.64.248:443 max_fails=10 fail_timeout=120s;
    server 45.76.71.198:443 max_fails=10 fail_timeout=120s;
}

server {
    listen 80 default_server;
    listen [::]:80 default_server;
    listen 443 default_server quic http3 reuseport;
    listen 443 default_server ssl http2;
    listen [::]:443 default_server quic http3 reuseport;
    listen [::]:443 default_server  ssl http2;
    server_name www.qhjack.cn qhjack.cn;
    #limit_req zone=server burst=10 nodelay;
    #limit_req_status 503;
    #limit_req_log_level error;

    #开启服务器推送
    http2_push_preload on;


    # 开启HTTP/3
    #add_header alt-svc 'quic=":443"; ma=86400; v="46,43",h3-27=":443";h3-23=":443"; ma=86400,h3-Q050=":443"; ma=86400,h3-Q049=":443"; ma=86400,h3-Q048=":443"; ma=86400,h3-Q046=":443"; ma=86400,h3-Q043=":443"; ma=86400,h3-T050=":443"; ma=86400';
    add_header alt-svc '$http3=":443"; ma=86400';
    add_header QUIC-Status $quic;

    quic_initial_max_streams_bidi 100;
    quic_retry on;
    # enable 0-RTT
    ssl_early_data on;
    ssl_session_tickets on;

    # 开启HTTPS/SSL和HSTS

    add_header Strict-Transport-Security "max-age=15768000;preload" always;

    ssl_certificate /etc/letsencrypt/live/qhjack.cn/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/qhjack.cn/privkey.pem;
    ssl_session_cache shared:SSL:1m;
    ssl_session_timeout 5m;
    ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4:!DH:!DHE;
    ssl_protocols TLSv1.1 TLSv1.2 TLSv1.3;
    ssl_prefer_server_ciphers on;
    ssl_stapling on;
    ssl_stapling_verify on;
    ssl_trusted_certificate /etc/letsencrypt/live/qhjack.cn/fullchain.pem;


    if ($scheme = http) {
        return 301 https://www.qhjack.cn$request_uri;
    }

    if ($host = "qhjack.cn") {
        return 301 https://www.qhjack.cn$request_uri;
    }


    #计算Cookie,判断是否缓存
    set $wordpress_auth 0;
    if ($http_cookie ~ ";(wordpress_logged_in_[a-z0-9]{32});") {
        set $wordpress_auth 1;
    }

    # POST requests and urls with a query string should always go to PHP
    if ($request_method = POST) {
        set $wordpress_auth 1;
    }
    if ($query_string != "") {
        set $wordpress_auth 1;
    }

    # Don't cache uris containing the following segments
    if ($request_uri ~* "/wp-admin/|/xmlrpc.php|wp-.*.php|/feed/|index.php|sitemap(_index)?.xml") {
        set $wordpress_auth 1;
    }

    # Don't use the cache for logged in users or recent commenters
    if ($http_cookie ~* "comment_author|wordpress_[a-f0-9]+|wp-postpass|wordpress_no_cache|wordpress_logged_in") {
        set $wordpress_auth 1;
    }

    location ~ \.(gif|jpg|jpeg|png|bmp|swf|js|css|pdf|doc)$ {        #指定缓存文件类型
        expires 7d;      #设置浏览器过期时间
        root /www/services/nginx/cache/proxy_store;
        proxy_pass https://qhjack-https;
        proxy_set_header Host $host;
        proxy_next_upstream error timeout invalid_header http_500 http_502 http_503 http_504;
        proxy_next_upstream_tries 5;
        proxy_store on; # 开启镜像加速功能
        proxy_store_access user:rw group:rw all:rw;       #缓存读写规则
    }

    location ~* /wp-content/plugins/versionpress/admin/public/.*$ {
        allow all;
    }

    location ~* /wp-content/plugins/versionpress/.*$ {
        deny all;
    }

    location ~* /wp-content/vpdb/.*$ {
        deny all;
    }

    location ~* /wp-content/vpbackups/.*$ {
        deny all;
    }

    location ~* /\.git/.*$ {
        deny all;
    }

    location / {
        proxy_pass https://qhjack-https;
        proxy_set_header Host $host;
        proxy_next_upstream error timeout invalid_header http_500 http_502 http_503 http_504;
        proxy_next_upstream_tries 5;
        proxy_cache_key "$scheme$request_method$host$request_uri$http_cookie";
        proxy_cache_use_stale error timeout invalid_header http_500;
        proxy_no_cache $wordpress_auth;
        proxy_cache_methods GET HEAD;
        proxy_cache_min_uses 1;
        proxy_cache_valid 200 1d;
        proxy_cache_valid 404 500 502 503 504 302 301 0s;
        proxy_cache_valid any 1d;
        proxy_cache qhjack_proxy_cache;
        etag  on;
        proxy_pass_header "X-Accel-Redirect";
        proxy_pass_header "X-Accel-Expires";
        proxy_pass_header "X-Accel-Charset";
        proxy_pass_header "X-Accel-Expires";
        proxy_pass_header "X-Accel-Limit-Rate";
    }

    charset utf-8;
    error_log /www/logs/qhjack/proxy/error.log;
    access_log /www/logs/qhjack/proxy/access.log access buffer=1k;


}

首先需要在listen开启http3选项和reuseport,需要注意的是,reuseport在配置中只能出现一次,否则会报错

还需要设置以下选项:

# 开启HTTP/3
add_header alt-svc '$http3=":443"; ma=86400';
add_header QUIC-Status $quic;

quic_initial_max_streams_bidi 100;
quic_retry on;
# enable 0-RTT
ssl_early_data on;
ssl_session_tickets on;

需要注意的是:

  1. $quic变量保存QUIC的状态信息,如果是QUIC传输,则这个变量为quic,否则,它就是空变量
  2. $http3变量保存支持的http3协议标准

运行

只需要使用如下命令就可以运行了,其他配置和nginx发布版本无异

sudo systemctl restart nginx

本文部分内容来自https://www.cnblogs.com/chenjinxinlove/p/10104854.html

0 0 投票数
文章评分
订阅评论
提醒
guest
0 评论
内联反馈
查看所有评论

扫码二维码快速访问本页

起航天空的http3探索 – 起航天空
0
希望看到您的想法,请发表评论。x
()
x