Nginx 配置TCP和UDP负载均衡
前言
Nginx除了以前常用的HTTP负载均衡外,Nginx增加基于TCP协议实现的负载均衡方法。
HTTP负载均衡,也就是我们通常所有“七层负载均衡”,工作在第七层“应用层”。而TCP负载均衡,就是我们通常所说的“四层负载均衡”,工作在“网络层”和“传输层”。例如,LVS(Linux Virtual Server,Linux虚拟服务)和F5(一种硬件负载均衡设备),也是属于“四层负载均衡”。
TCP负载均衡配置
Nginx从1.9.0版本开始,新增加了一个stream模块,用来实现四层协议的转发、代理或者负载均衡等鉴于Nginx在负载均衡和web service上的成功,和Nginx良好的框架,stream模块前景一片光明。
Nginx的stream模块默认不会自带安装,需要编译安装的时候手动添加上这个模块,不过我的系统里是已经安装了此模块,如果你没有安装,可能需要通过官网下载源码来打开此模块。
nginx使用ngx_stream_core_module模块代理tcp长连接短连接,可以增强服务器的容灾能力。
TCP负载均衡的执行原理
当Nginx从监听端口收到一个新的客户端链接时,立刻执行路由调度算法,获得指定需要连接的服务IP,然后创建一个新的上游连接,连接到指定服务器。
TCP负载均衡支持Nginx原有的调度算法,包括Round Robin(默认,轮询调度),哈希(选择一致)等。同时,调度信息数据也会和健壮性检测模块一起协作,为每个连接选择适当的目标上游服务器。如果使用Hash负载均衡的调度方法,你可以使用$remote_addr(客户端IP)来达成简单持久化会话(同一个客户端IP的连接,总是落到同一个服务server上)。
和其他upstream模块一样,TCP的stream模块也支持自定义负载均和的转发权重(配置“weight=2”),还有backup和down的参数,用于踢掉失效的上游服务器。max_conns参数可以限制一台服务器的TCP连接数量,根据服务器的容量来设置恰当的配置数值,尤其在高并发的场景下,可以达到过载保护的目的。
Nginx监控客户端连接和上游连接,一旦接收到数据,则Nginx会立刻读取并且推送到上游连接,不会做TCP连接内的数据检测。Nginx维护一份内存缓冲区,用于客户端和上游数据的写入。如果客户端或者服务端传输了量很大的数据,缓冲区会适当增加内存的大小。
当Nginx收到任意一方的关闭连接通知,或者TCP连接被闲置超过了proxy_timeout配置的时间,连接将会被关闭。对于TCP长连接,我们更应该选择适当的proxy_timeout的时间,同时,关注监听socke的so_keepalive参数,防止过早地断开连接。
服务健壮性监控
TCP负载均衡模块支持内置健壮性检测,一台上游服务器如果拒绝TCP连接超过proxy_connect_timeout配置的时间,将会被认为已经失效。在这种情况下,Nginx立刻尝试连接upstream组内的另一台正常的服务器。连接失败信息将会记录到Nginx的错误日志中。
如果一台服务器,反复失败(超过了max_fails或者fail_timeout配置的参数),Nginx也会踢掉这台服务器。服务器被踢掉60秒后,Nginx会偶尔尝试重连它,检测它是否恢复正常。如果服务器恢复正常,Nginx将它加回到upstream组内,缓慢加大连接请求的比例。
之所“缓慢加大”,因为通常一个服务都有“热点数据”,也就是说,80%以上甚至更多的请求,实际都会被阻挡在“热点数据缓存”中,真正执行处理的请求只有很少的一部分。在机器刚刚启动的时候,“热点数据缓存”实际上还没有建立,这个时候爆发性地转发大量请求过来,很可能导致机器无法“承受”而再次挂掉。以mysql为例子,我们的mysql查询,通常95%以上都是落在了内存cache中,真正执行查询的并不多。
其实,无论是单台机器或者一个集群,在高并发请求场景下,重启或者切换,都存在这个风险,解决的途径主要是两种:
(1)请求逐步增加,从少到多,逐步积累热点数据,最终达到正常服务状态。
(2)提前准备好“常用”的数据,主动对服务做“预热”,预热完成之后,再开放服务器的访问。
下面是一个配置信息,自己也方便记录一下:
这里我配置了两个代理,一个是转发6000端口,一个转发6020端口到后台的两个服务器。
以下是我的配置:
其中加了stream节点,需要转发那几个端口,直接在stream的子节点下面配置server的信息。
user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log;
pid /run/nginx.pid;
# Load dynamic modules. See /usr/share/doc/nginx/README.dynamic.
include /usr/share/nginx/modules/*.conf;
events {
worker_connections 65535;
}
stream{
#tcp
upstream loginserver_android{
#ip_hash;
server localhost:6001 max_fails=1 fail_timeout=10s;
server localhost:6002 max_fails=1 fail_timeout=10s;
server localhost:6003 max_fails=1 fail_timeout=10s;
#server localhost:6004 max_fails=1 fail_timeout=10s;
#server localhost:6005 max_fails=1 fail_timeout=10s;
#server localhost:6006 max_fails=1 fail_timeout=10s;
}
upstream loginserver_ios{
#ip_hash;
# server localhost:6001 max_fails=1 fail_timeout=10s;
# server localhost:6002 max_fails=1 fail_timeout=10s;
# server localhost:6003 max_fails=1 fail_timeout=10s;
server localhost:6004 max_fails=1 fail_timeout=10s;
server localhost:6005 max_fails=1 fail_timeout=10s;
server localhost:6006 max_fails=1 fail_timeout=10s;
}
server {
listen 6020;
proxy_connect_timeout 5s;
proxy_timeout 10m;
###proxy_timeout 24h;
###这个参数在使用EMQ压力测试时很有用,刚开始设备的超时时间过段,导致测试一小会儿就出现 {shutdown,connack_timeout}
proxy_pass loginserver_ios;
}
server {
listen 6000;
proxy_connect_timeout 5s;
proxy_timeout 10m;
###proxy_timeout 24h;
###这个参数在使用EMQ压力测试时很有用,刚开始设备的超时时间过段,导致测试一小会儿就出现 {shutdown,connack_timeout}
proxy_pass loginserver_android;
}
}
UDP负载均衡配置
Nginx 1.9.13开始支持UDP负载匀衡,现代应用通常使用多种协议,很多核心Internet协议都早于HTTP,支持UDP势在必行。
UDP常用于非事务性的轻量级协议,如:DNS、syslog、RADIUS。
这些协议对可靠性没有严格要求,若UDP消息(数据报)丢失,客户端可在超时后重新发送。
# Load balance UDP-based DNS traffic across two servers
stream {
upstream dns_upstreams {
server 192.168.136.130:53;
server 192.168.136.131:53;
}
server {
listen 53 udp;
proxy_pass dns_upstreams;
proxy_timeout 1s;
proxy_responses 1;
error_log logs/dns.log;
}
}
NGINX在53端口接收到UDP数据报,使用负载平衡算法(默认:轮询/Round Robin)选择后端服务,等待后端服务响应,并将响应返回客户端。
若后端服务无法响应,NGINX将其标记为“失败”,并暂停向此服务发送数据报。每隔几秒钟,NGINX会向服务器发送较小流量检查服务状态,确认服务是否恢复。
UDP不保证数据的端到端传递,要求客户端能够处理网络级错误和重传。
当客户端无法连接到首选服务器时,则必须等待超时才能尝试其他服务器。这会在UDP事务中引入冗长的延迟。
NGINX高可用性和负载平衡可消除或减少此类延迟。
客户端将UDP请求发送到NGINX,负载均衡器监视UDP服务器运行状况和可用性,避免将请求发送到故障或过载服务器。