下圖是 nginx upstream keepalive 長連接的實現(xiàn)原理。首先每個進程需要一個 connection pool,里面都是長連接,多進程之間是不需要共享這個連接池的。 一旦與后端服務(wù)器建立連接,則在當(dāng)前請求連接結(jié)束之后不會立即關(guān)閉連接,而是把用完的連接保存在一個 keepalive connection pool 里面,以后每次需要建立向后連接的時候,只需要從這個連接池里面找。
如果找到合適的連接的話,就可以直接來用這個連接,不需要重新創(chuàng)建 socket 或者發(fā)起 connect()。這樣既省下建立連接時在握手的時間消耗,又可以避免 TCP 連接的 slow start 慢啟動。如果在 keepalive 連接池找不到合適的連接,那就按照原來的步驟重新建立連接。
如果你的連接池的數(shù)控制在 128 個,總共線程池內(nèi)的線程數(shù)是 128 * nginx worker 個,但因為你要應(yīng)對更多的并發(fā)請求,所以臨時又加了很多的連接,但這臨時的連接是短連接和長連接要看你的 Nginx 版本。那他如何被收回,兩地保證,一點是他會主動去釋放,另一點是 keepalive timeout 的時間。
需要注意的是,使用 DockerHub 中的 Nginx 鏡像是不用二次編譯的。
# 下載1.9版本以上的nginx wget http://nginx.org/download/nginx-1.20.2.tar.gz # 安裝依賴包 yum install -y gcc glibc gcc-c++ prce-devel openssl-devel pcre-devel useradd -s /sbin/nologin nginx -M # 加壓目錄 tar xf nginx-1.10.3.tar.gz && cd nginx-1.20.2 # 手動編譯 ./configure \ --prefix=/usr/local/nginx-1.20.2 \ --user=nginx \ --group=nginx \ --with-http_ssl_module \ --with-http_stub_status_module \ --with-stream # 安裝到系統(tǒng)上 make && make install # 檢查配置文件 /usr/local/nginx/sbin/nginx -t
以下的配置就是 TCP 轉(zhuǎn)發(fā)的最簡配置
我們能很明顯的發(fā)現(xiàn),stream 模塊的配置其實跟 http 模塊很類似。但實際上 stream 模塊與 http 模塊上完全是兩套不同的處理流程。用最簡單的說法就是,http 模塊是基于 Layer7 層的應(yīng)用層處理流程,而 Stream 僅在 Layer4 層上對連接進行處理。
所以 stream 模塊無法像 http 模塊那樣能區(qū)分 vhost 主機名,然而這在 stream 模塊在引入了 ssl 配置之后又能支持了。而且 stream 模塊還能引入 ssl/tls 來對 TCP 連接進行加密。由于 TLS 標(biāo)準(zhǔn)內(nèi)對 SNI 提供了支持,所以又能識別主機名了。在理論上,stream 模塊的端口轉(zhuǎn)發(fā)效率實際上相比 http 模塊的反向代理效率更高。
user nginx; worker_processes 1; events { worker_connections 1024; } stream { # 全局配置 preread_timeout 120s; proxy_connect_timeout 120s; proxy_protocol_timeout 120s; resolver_timeout 120s; proxy_timeout 120s; tcp_nodelay on; # 設(shè)置日志格式 log_format proxy '$remote_addr [$time_local] ' '$protocol $status $bytes_sent $bytes_received ' '$session_time "$upstream_addr" "$upstream_bytes_sent"' '"$upstream_bytes_received" "$upstream_connect_time"'; access_log /var/log/nginx/stream.access.log proxy; error_log /var/log/nginx/stream.error.log error; upstream app_pg { hash $remote_addr consistent; server 192.168.100.60:5432; server 192.168.100.61:5432; server 192.168.100.62:5432; } server { # 不指定協(xié)議默認(rèn)是TCP協(xié)議 listen 127.0.0.1:5432 so_keepalive=on; proxy_pass app_pg; } server{ # keepalive的可配置參數(shù)差不多有以下幾個:keepidle,keepintvl,keepcnt # keepidle為連接保持時間;keepintvl為連接的間隔時間;keepcnt是連接的個數(shù) # 下示將idle超時設(shè)置為30分鐘,探測間隔為系統(tǒng)默認(rèn)值,并將探測計數(shù)設(shè)置為10個探測器 # 實際配置的格式為:so_keepalive=on|off|[keepidle]:[keepintvl]:[keepcnt] listen *:3306 so_keepalive=30m::10; proxy_connect_timeout 10s; proxy_timeout 20s; proxy_buffer_size 512k; proxy_pass 192.168.100.60:8000; } }
user nginx; worker_processes 1; events { worker_connections 1024; } stream { upstream ssh { hash $remote_addr consistent; server 192.168.1.42:22 weight=5; } server { listen 2222; proxy_pass ssh; } }
以下的配置就是 UDP 轉(zhuǎn)發(fā)的最簡配置
user nginx; worker_processes 1; events { worker_connections 1024; } stream { # 全局配置 proxy_timeout 120s; tcp_nodelay on; # 設(shè)置日志格式 log_format proxy '$remote_addr [$time_local] ' '$protocol $status $bytes_sent $bytes_received ' '$session_time "$upstream_addr" ' '"$upstream_bytes_sent" "$upstream_bytes_received" "$upstream_connect_time"'; access_log /var/log/nginx/stream.access.log proxy; error_log /var/log/nginx/stream.error.log error; # 配置dns負(fù)載均衡 upstream dns_upstreams { server 1.1.1.1:53 weight=1; server 1.0.0.1:53 weight=1; # weight負(fù)載均衡權(quán)重 server 8.8.8.8:53 weight=1 backup; # backup標(biāo)記為備用服務(wù)器 } server{ listen 53 udp; proxy_responses 1; # UDP協(xié)議專用;期望后端返回給客戶端數(shù)據(jù)包的數(shù)量 proxy_timeout 20s; # 超時時間 proxy_pass dns_upstreams; } }