Nginx-3 Nginx 的负载均衡策略
Nginx 的负载均衡其实就是指将请求按照一定的策略转发给服务集群中的一台,提高了服务集群的可用性,解决数据流量过大、网络负荷过重的问题。
AKF 扩展立方体
分为 3 个方向负载:
- x 轴:增加实例数量,一般要求应用是无状态的,通过增加应用数量来提高应用程序性能和可用性,但是由于数据库等资源的限制,所以并不是无限制的
- y 轴:基于业务功能拓展,不同的业务领域扩展成不同的应用,通过 url 或其他方式将请求转发给不同的应用,同时每个应用还可以进行 x 轴拓展
- z轴:按数据属性分区,比如应用有多个数据中心,可以基于用户的 ip 选择比较近的数据中心进行服务
顺便一提,一般来讲,nginx 与后端应用服务通常部署在同一内网环境中,且后端服务一般是有限的,开启 keepalive 长连接对于性能(提升吞吐量,降低时延)的提升会更明显
Syntax: keepalive connections;
Default: —
Context: upstream
Syntax: keepalive_requests number;
Default:
keepalive_requests 1000;
Context: upstream
Syntax: keepalive_time time;
Default:
keepalive_time 1h;
Context: upstream
Syntax: keepalive_timeout timeout;
Default:
keepalive_timeout 60s;
Context: upstream
round-robin
策略:加权轮询访问 server 指令指定的上游服务
指令:
weight # 服务器权重,默认 1
max_conns # server 的最大并发连接数,仅作用于单 worker,默认为 0 没有限制
max_fails # 在 fail_timeout 时间段内,允许的最大失败次数
# 如果达到该次数则在 fail_timeout 这段时间内不再选择此服务器
fail_timeout # 到达 max_fails 后,该 server 的不能再次访问时间
配置两台 nginx
# 模拟两台后端服务
server {
listen 8011;
server_name localhost;
return 200 '8011 server response \n';
}
server {
listen 8012;
server_name localhost;
return 200 '8012 server response \n';
}
# 反向代理服务器
upstream rrups {
server localhost:8011 weight=2 max_conns=2 max_fails=2 fail_timeout=5;
server localhost:8012 weight=1;
keepalive 32;
}
server {
listen 9001;
location / {
proxy_pass http://rrups;
proxy_http_version 1.1;
proxy_set_header Connection "";
}
}
测试
[root@node-17 ~]# curl 123.207.214.107:9001
8011 server response
[root@node-17 ~]# curl 123.207.214.107:9001
8011 server response
[root@node-17 ~]# curl 123.207.214.107:9001
8012 server response
[root@node-17 ~]# curl 123.207.214.107:9001
8011 server response
[root@node-17 ~]# curl 123.207.214.107:9001
8011 server response
[root@node-17 ~]# curl 123.207.214.107:9001
8012 server response
抓下包进行分析:
tcpdump -i lo port 8011
09:14:30.666190 IP VM-16-11-centos.60192 > VM-16-11-centos.8011: Flags [S], seq 976418546, win 43690, options [mss 65495,sackOK,TS val 1034055580 ecr 0,nop,wscale 7], length 0
09:14:30.666208 IP VM-16-11-centos.8011 > VM-16-11-centos.60192: Flags [S.], seq 1448846227, ack 976418547, win 43690, options [mss 65495,sackOK,TS val 1034055580 ecr 1034055580,nop,wscale 7], length 0
09:14:30.666219 IP VM-16-11-centos.60192 > VM-16-11-centos.8011: Flags [.], ack 1, win 342, options [nop,nop,TS val 1034055580 ecr 1034055580], length 0
09:14:30.666253 IP VM-16-11-centos.60192 > VM-16-11-centos.8011: Flags [P.], seq 1:70, ack 1, win 342, options [nop,nop,TS val 1034055580 ecr 1034055580], length 69
09:14:30.666266 IP VM-16-11-centos.8011 > VM-16-11-centos.60192: Flags [.], ack 70, win 342, options [nop,nop,TS val 1034055580 ecr 1034055580], length 0
09:14:30.666349 IP VM-16-11-centos.8011 > VM-16-11-centos.60192: Flags [P.], seq 1:185, ack 70, win 342, options [nop,nop,TS val 1034055580 ecr 1034055580], length 184
09:14:30.666356 IP VM-16-11-centos.60192 > VM-16-11-centos.8011: Flags [.], ack 185, win 350, options [nop,nop,TS val 1034055580 ecr 1034055580], length 0
09:14:32.105110 IP VM-16-11-centos.60192 > VM-16-11-centos.8011: Flags [P.], seq 70:139, ack 185, win 350, options [nop,nop,TS val 1034057019 ecr 1034055580], length 69
09:14:32.105183 IP VM-16-11-centos.8011 > VM-16-11-centos.60192: Flags [P.], seq 185:369, ack 139, win 342, options [nop,nop,TS val 1034057019 ecr 1034057019], length 184
09:14:32.105192 IP VM-16-11-centos.60192 > VM-16-11-centos.8011: Flags [.], ack 369, win 359, options [nop,nop,TS val 1034057019 ecr 1034057019], length 0
09:15:32.159360 IP VM-16-11-centos.60192 > VM-16-11-centos.8011: Flags [F.], seq 139, ack 369, win 359, options [nop,nop,TS val 1034117073 ecr 1034057019], length 0
09:15:32.159429 IP VM-16-11-centos.8011 > VM-16-11-centos.60192: Flags [F.], seq 369, ack 140, win 342, options [nop,nop,TS val 1034117073 ecr 1034117073], length 0
09:15:32.159439 IP VM-16-11-centos.60192 > VM-16-11-centos.8011: Flags [.], ack 370, win 359, options [nop,nop,TS val 1034117073 ecr 1034117073], length 0
分析 TCP 标志位
只在最开始 [S] [S.] [.] (syn syn-ack ack)建立了一次连接,两次请求复用了同一个连接,1分钟后连接断开
hash
ip_hash
策略:以客户端IP
作为哈希算法的关键字,映射到特定的上游服务器中。
- 对 IPV4 地址使用前3个字节作为关键字,对于 IPV6 地址则使用完整地址
- 可以结合 realip 模块修改用于执行算法的 IP 地址
配置:
upstream iphashups {
ip_hash;
server localhost:8011 weight=2 max_conns=2 max_fails=2 fail_timeout=5;
server localhost:8012 weight=1;
}
server {
listen 9002;
location / {
proxy_pass http://iphashups;
proxy_http_version 1.1;
proxy_set_header Connection "";
}
}
测试
[root@node-17 ~]# curl 123.207.214.107:9002
8011 server response
[root@node-17 ~]# curl 123.207.214.107:9002
8011 server response
[root@node-17 ~]# curl 123.207.214.107:9002
8011 server response
可以发现如果配置了 ip_hash,及时配置了 weight 权重,也一直使用同一台 server
nginx 中的 upstream_hash 模块支持指定关键字作为 hash 算法的 key,不再局限于 ip 地址
配置:
upstream hashups {
hash user_$arg_username;
server localhost:8011 weight=2 max_conns=2 max_fails=2 fail_timeout=5;
server localhost:8012 weight=1;
}
server {
listen 9003;
location / {
proxy_pass http://hashups;
proxy_http_version 1.1;
proxy_set_header Connection "";
}
}
测试:
[root@VM-16-11-centos nginx]# curl 123.207.214.107:9003?username=zhangsan
8012 server response
[root@VM-16-11-centos nginx]# curl 123.207.214.107:9003?username=zhangsan
8012 server response
[root@VM-16-11-centos nginx]# curl 123.207.214.107:9003?username=zhangsan
8012 server response
[root@VM-16-11-centos nginx]# curl 123.207.214.107:9003?username=lisi
8011 server response
[root@VM-16-11-centos nginx]# curl 123.207.214.107:9003?username=lisi
8011 server response
[root@VM-16-11-centos nginx]# curl 123.207.214.107:9003?username=lisi
8011 server response
一致性 hash算法
当使用 hash 算法时,如果有某台上游服务宕机了,我们一般不能直接将其从 upstream 中移除,原因是简单的 hash 其实就是计算一个哈希值,对服务数量取余,如果突然增加或减少一台服务器,会有大量的正常连接受到影响,一致性 hash 算法就是用来缓解这个问题的。
具体的原理可以参考这篇文章:一致性哈希
使用的话其实比较简单,在 hash 对应的 key 后加 consistent 即可
Syntax: hash key [consistent];
Default: —
Context: upstream
upstream hashups {
hash user_$arg_username consistent;
server localhost:8011 weight=2 max_conns=2 max_fails=2 fail_timeout=5;
server localhost:8012 weight=1;
}
least-conn
策略:从所有上游服务器中,找出一个当前连接数最少的,如果出现多个最少连接数相同的,就选择 round-robin 算法
配置:
upstream hashups {
least_conn;
server localhost:8011 weight=2 max_conns=2 max_fails=2 fail_timeout=5;
server localhost:8012 weight=1;
}
random
策略:从所有上游服务器中,随机选择一台服务器来处理请求
配置:
upstream hashups {
random;
server localhost:8011 weight=2 max_conns=2 max_fails=2 fail_timeout=5;
server localhost:8012 weight=1;
}
由于它没有考虑服务器的负载情况,通常在实际生产环境中比较少用,尤其是在需要均衡负载的场景下
值得注意的一点是,如果 nginx 启动了多台 worker 进程,需要借助共享内存使得负载均衡策略对所有 worker 进程生效
Syntax: zone name [size];
Default: —
Context: upstream
This directive appeared in version 1.9.0.
另外 upstream 之间的模块也是有顺序的,文件来源 (objs/ngx_modules.c)
ngx_module_t *ngx_modules[] = {
......
&ngx_http_upstream_hash_module,
&ngx_http_upstream_ip_hash_module,
&ngx_http_upstream_least_conn_module,
&ngx_http_upstream_random_module,
&ngx_http_upstream_keepalive_module,
&ngx_http_upstream_zone_module,
......
NULL
};
从上至下的顺序,worker 进程在负载均衡算法的最后会将信息维护在共享内存中:
- 后端服务器的状态:如健康检查的结果、服务器是否可用等
- 负载均衡算法:例如轮询、最少连接、哈希算法等
- 请求的分配情况:nginx 会将请求分配到最合适的后端服务器上,并且通过共享内存来更新各个服务器的状态(例如连接数、响应时间等)