好记性,比如烂笔头
自
version 1.9.0
以来新增了ngx_stream_core_module
模块,使nginx
支持四层负载均衡。这个模块不仅能实现TCP
和UDP
转发,还能支持负载均衡upstream
配置。默认编译的时候该模块并未编译进去,需要编译的时候添加--with-stream
参数使其支持stream
代理。想要了解详情使用技巧和方式,可以参考 官方文档 stream 模块 地址。由于协议的不同,
UDP
转发仅会将请求包转发至目标,UDP
回复包将由目标机器直接发出给你的请求端。这个回复包是不会经过转发端的,所以在你转发目标端IP
已经被不明设备给屏蔽无法连接的话,这个UDP
回复包你还是收不到,这并不是Nginx
的问题。
下图是
nginx upstream keepalive
长连接的实现原理。首先每个进程需要一个connection pool
,里面都是长连接,多进程之间是不需要共享这个连接池的。 一旦与后端服务器建立连接,则在当前请求连接结束之后不会立即关闭连接,而是把用完的连接保存在一个keepalive connection pool
里面,以后每次需要建立向后连接的时候,只需要从这个连接池里面找。如果找到合适的连接的话,就可以直接来用这个连接,不需要重新创建
socket
或者发起connect()
。这样既省下建立连接时在握手的时间消耗,又可以避免TCP
连接的slow start
慢启动。如果在keepalive
连接池找不到合适的连接,那就按照原来的步骤重新建立连接。如果你的连接池的数控制在
128
个,总共线程池内的线程数是128 * nginx worker
个,但因为你要应对更多的并发请求,所以临时又加了很多的连接,但这临时的连接是短连接和长连接要看你的Nginx
版本。那他如何被收回,两地保证,一点是他会主动去释放,另一点是keepalive timeout
的时间。
1. 手动编译
需要注意的是,使用 DockerHub 中的 Nginx 镜像是不用二次编译的。
# 下载1.9版本以上的nginx
wget http://nginx.org/download/nginx-1.10.3.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.10.3
# 手动编译
./configure \
--prefix=/usr/local/nginx-1.10.3 \
--user=nginx \
--group=nginx \
--with-http_ssl_module \
--with-http_stub_status_module \
--with-stream
# 安装到系统上
make && make install
# 检查配置文件
/usr/local/nginx/sbin/nginx -t
2. TCP 转发
以下的配置就是 TCP 转发的最简配置
我们能很明显的发现,
stream
模块的配置其实跟http
模块很类似。但实际上stream
模块与http
模块上完全是两套不同的处理流程。用最简单的说法就是,http
模块是基于Layer7
层的应用层处理流程,而Stream
仅在Layer4
层上对连接进行处理。所以
stream
模块无法像http
模块那样能区分vhost
主机名,然而这stream
模块在引入了ssl
配置之后又能支持了。而且stream
模块还能引入ssl/tls
来对TCP
连接进行加密。由于TLS
标准内对SNI
提供了支持,所以又能识别主机名了。在理论上,stream
模块的端口转发效率实际上相比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;
# 设置日志格式
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 {
# 不指定协议默认是TCP协议
listen 127.0.0.1:5432 so_keepalive=on;
proxy_pass app_pg;
}
server{
# keepalive的可配置参数差不多有以下几个:keepidle,keepintvl,keepcnt
# keepidle为连接保持时间;keepintvl为连接的间隔时间;keepcnt是连接的个数
# 下示将idle超时设置为30分钟,探测间隔为系统默认值,并将探测计数设置为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;
}
}
- 实现 SSH 转发
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;
}
}
3. UDP 转发
以下的配置就是 UDP 转发的最简配置
UDP
转发并不是stream
模块一开始就支持的,而是在1.9.3
版本之后的stream
模块才追加了UDP
转发支持,所以要配置UDP
转发前必须得先确定自己的Nginx
版本是否达到了要求。
user nginx;
worker_processes 1;
events {
worker_connections 1024;
}
stream {
# 全局配置
proxy_timeout 120s;
tcp_nodelay on;
# 设置日志格式
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负载均衡
upstream dns_upstreams {
server 1.1.1.1:53 weight=1;
server 1.0.0.1:53 weight=1; # weight负载均衡权重
server 8.8.8.8:53 weight=1 backup; # backup标记为备用服务器
}
server{
listen 53 udp;
proxy_responses 1; # UDP协议专用;期望后端返回给客户端数据包的数量
proxy_timeout 20s; # 超时时间
proxy_pass dns_upstreams;
}
}
4. 总结说明
在工作中掌握了,才是真的明白了。
虽然 Nginx
在超高并发的情况下也是有着资源占用过高的问题,但实际上放在生产环境里并没有什么太大问题。当然可能性能还是达不到 Haproxy
的水平,但对于我们一般使用而言,Nginx
足矣胜任大部分需求。还有就是需要注意,转发当中使用的配置选项,有时候真的很要人命。