阿里云-云小站(无限量代金券发放中)
【腾讯云】云服务器、云数据库、COS、CDN、短信等热卖云产品特惠抢购

Nginx 的软件负载均衡详解

31次阅读
没有评论

共计 4628 个字符,预计需要花费 12 分钟才能阅读完成。

负载均衡在服务端开发中算是一个比较重要的特性。因为 Nginx 除了作为常规的 Web 服务器外,还会被大规模的用于反向代理前端,因为 Nginx 的异步框架可以处理很大的并发请求,把这些并发请求 hold 住之后就可以分发给后台服务端 (backend servers,也叫做服务池,后面简称 backend) 来做复杂的计算、处理和响应,这种模式的好处是相当多的:隐藏业务主机更安全,节约了公网 IP 地址,并且在业务量增加的时候可以方便地扩容后台服务器。

负载均衡可以分为硬件负载均衡和软件负载均衡,前者一般是专用的软件和硬件相结合的设备,设备商会提供完整成熟的解决方案,通常也会更加昂贵。软件的负载均衡以 Nginx 占据绝大多数,本文也是基于其手册做相应的学习研究的。

一、基本简介

负载均衡涉及到以下的基础知识。

(1) 负载均衡算法

阿里云 2 核 2G 服务器 3M 带宽 61 元 1 年,有高配

腾讯云新客低至 82 元 / 年,老客户 99 元 / 年

代金券:在阿里云专用满减优惠券

(2) 会话一致性

用户 (浏览器) 在和服务端交互的时候,通常会在本地保存一些信息,而整个过程叫做一个会话 (Session) 并用唯一的 Session ID 进行标识。会话的概念不仅用于购物车这种常见情况,因为 HTTP 协议是无状态的,所以任何需要逻辑上下文的情形都必须使用会话机制,此外 HTTP 客户端也会额外缓存一些数据在本地,这样就可以减少请求提高性能了。如果负载均衡可能将这个会话的请求分配到不同的后台服务端上,这肯定是不合适的,必须通过多个 backend 共享这些数据,效率肯定会很低下,最简单的情况是保证会话一致性——相同的会话每次请求都会被分配到同一个 backend 上去。

(3) 后台服务端的动态配置

出问题的 backend 要能被及时探测并剔除出分配群,而当业务增长的时候可以灵活的添加 backend 数目。此外当前风靡的 Elastic Compute 云计算服务,服务商也应当根据当前负载自动添加和减少 backend 主机。

(4) 基于 DNS 的负载均衡

通常现代的网络服务者一个域名会关连到多个主机,在进行 DNS 查询的时候,默认情况下 DNS 服务器会以 round-robin 形式以不同的顺序返回 IP 地址列表,因此天然将客户请求分配到不同的主机上去。不过这种方式含有固有的缺陷:DNS 不会检查主机和 IP 地址的可访问性,所以分配给客户端的 IP 不确保是可用的(Google 404);DNS 的解析结果会在客户端、多个中间 DNS 服务器不断的缓存,所以 backend 的分配不会那么的理想。

二、Nginx 中的负载均衡

Nginx 中的负载均衡配置在手册中描述的极为细致,此处就不流水帐了。对于常用的 HTTP 负载均衡,主要先定义一个 upstream 作为 backend group,然后通过 proxy_pass/fastcgi_pass 等方式进行转发操作,其中 fastcgi_pass 几乎算是 Nginx+PHP 站点的标配了。

2.1 会话一致性

Nginx 中的会话一致性是通过 sticky 开启的,会话一致性和之前的负载均衡算法之间并不冲突,只是需要在第一次分配之后,该会话的所有请求都分配到那个相同的 backend 上面。目前支持三种模式的会话一致性:

(1). Cookie Insertion
在 backend 第一次 response 之后,会在其头部添加一个 session cookie,即由负载均衡器向客户端植入 cookie,之后客户端接下来的请求都会带有这个 cookie 值,Nginx 可以根据这个 cookie 判断需要转发给哪个 backend 了。

sticky cookie srv_id expires=1h domain=.example.com path=/;

上面的 srv_id 代表了 cookie 的名字,而后面的参数 expires、domain、path 都是可选的。

(2). Sticky Routes
也是在 backend 第一次 response 之后,会产生一个 route 信息,route 信息通常会从 cookie/URI 信息中提取。

route $route_cookie $route_uri;

这样 Nginx 会按照顺序搜索 routecookie、route_uri 参数并选择第一个非空的参数用作 route,而如果所有的参数都是空的,就使用上面默认的负载均衡算法决定请求分发给哪个 backend。

(3). Learn
较为的复杂也较为的智能,Nginx 会自动监测 request 和 response 中的 session 信息,而且通常需要回话一致性的请求、应答中都会带有 session 信息,这和第一种方式相比是不用增加 cookie,而是动态学习已有的 session。

这种方式需要使用到 zone 结构,在 Nginx 中 zone 都是共享内存,可以在多个 worker process 中共享数据用的。(不过其他的会话一致性怎么没用到共享内存区域呢?)

learn 
   create=$upstream_cookie_examplecookie
   lookup=$cookie_examplecookie
   zone=client_sessions:1m
   timeout=1h;
2.2 Session Draining

主要是有需要关闭某些 backend 以便维护或者升级,这些关键性的服务都讲求 gracefully 处理的:就是新的请求不会发送到这个 backend 上面,而之前分配到这个 backend 的会话的后续请求还会继续发送给他,直到这个会话最终完成。

让某个 backend 进入 draining 的状态,既可以直接修改配置文件,然后按照之前的方式通过向 master process 发送信号重新加载配置,也可以采用 Nginx 的 on-the-fly 配置方式。

$ curl http://localhost/upstream_conf?upstream=backend
$ curl http://localhost/upstream_conf?upstream=backend\&id=1\&drain=1

通过上面的方式,先列出各个 bacnkend 的 ID 号,然后 drain 指定 ID 的 backend。通过在线观测 backend 的所有 session 都完成后,该 backend 就可以下线了。

2.3 backend 健康监测

backend 出错会涉及到两个参数,max_fails=1 fail_timeout=10s; 意味着只要 Nginx 向 backend 发送一个请求失败或者没有收到一个响应,就认为该 backend 在接下来的 10s 是不可用的状态。

通过周期性地向 backend 发送特殊的请求,并期盼收到特殊的响应,可以用以确认 backend 是健康可用的状态。通过 health_check 可以做出这个配置。

match server_ok {
    status 200-399;
    header Content-Type = text/html;
    body !~ "maintenance mode";
}
server {
    location / {
        proxy_pass http://backend;
        health_check interval=10 fails=3 passes=2 match=server_ok;
    }
}

上面的 health_check 是必须的,后面的参数都是可选的。尤其是后面的 match 参数,可以自定义服务器健康的条件,包括返回状态码、头部信息、返回 body 等,这些条件是 && 与关系。默认情况下 Nginx 会相隔 interval 的间隔向 backend group 发送一个”/“的请求,如果超时或者返回非 2xx/3xx 的响应码,则认为对应的 backend 是 unhealthy 的,那么 Nginx 会停止向其发送 request 直到下次改 backend 再次通过检查。

在使用了 health_check 功能的时候,一般都需要在 backend group 开辟一个 zone,在共享 backend group 配置的同时,所有 backend 的状态就可以在所有的 worker process 所共享了,否则每个 worker process 独立保存自己的状态检查计数和结果,两种情况会有很大的差异哦。

2.4 通过 DNS 设置 HTTP 负载均衡

Nginx 的 backend group 中的主机可以配置成域名的形式,如果在域名的后面添加 resolve 参数,那么 Nginx 会周期性的解析这个域名,当域名解析的结果发生变化的时候会自动生效而不用重启。

http {
    resolver 10.0.0.1 valid=300s ipv6=off;
    resolver_timeout 10s;
    server {
        location / {proxy_pass http://backend;}
    }
    upstream backend {
        zone backend 32k;
        least_conn;
        ...
        server backend1.example.com resolve;
        server backend2.example.com resolve;
    }
}

如果域名解析的结果含有多个 IP 地址,这些 IP 地址都会保存到配置文件中去,并且这些 IP 都参与到自动负载均衡。

2.5 TCP/UDP 流量的负载均衡

通常,HTTP 和 HTTPS 的负载均衡叫做七层负载均衡,而 TCP 和 UDP 协议的负载均衡叫做四层负载均衡。因为七层负载均衡通常都是 HTTP 和 HTTPS 协议,所以这种负载均衡相当于是四层负载均衡的特例化,均衡器可以根据 HTTP/HTTPS 协议的头部(User-Agent、Language 等)、响应码甚至是响应内容做额外的规则,达到特定条件特定目的的 backend 转发的需求。

除了 Nginx 所专长的 HTTP 负载均衡,Nginx 还支持 TCP 和 UDP 流量的负载均衡,适用于 LDAP/MySQL/RTMP 和 DNS/syslog/RADIUS 各种应用场景。这类情况的负载均衡使用 stream 来配置,Nginx 编译的时候需要支持–with-stream 选项。查看手册,其配置原理和参数和 HTTP 负载均衡差不多。

因为 TCP、UDP 的负载均衡都是针对通用程序的,所以之前 HTTP 协议支持的 match 条件 (status、header、body) 是没法使用的。TCP 和 UDP 的程序可以根据特定的程序,采用 send、expect 的方式来进行动态健康检测。

match http {
    send      "GET / HTTP/1.0\r\nHost: localhost\r\n\r\n";
    expect ~* "200 OK";
}
2.6 其他特性

slow_start=30s:防止新添加 / 恢复的主机被突然增加的请求所压垮,通过这个参数可以让该主机的 weight 从 0 开始慢慢增加到设定值,让其负载有一个缓慢增加的过程。

max_conns=30:可以设置 backend 的最大连接数目,当超过这个数目的时候会被放到 queue 队列中,同时队列的大小和超时参数也可以设置,当队列中的请求数大于设定值,或者超过了 timeout 但是 backend 还不能处理请求,则客户端将会收到一个错误返回。通常来说这还是一个比较重要的参数,因为 Nginx 作为反向代理的时候,通常就是用于抗住并发量的,如果给 backend 过多的并发请求,很可能会占用后端过多的资源(比如线程、进程非事件驱动),最终反而会影响 backend 的处理能力。

阿里云 2 核 2G 服务器 3M 带宽 61 元 1 年,有高配

腾讯云新客低至 82 元 / 年,老客户 99 元 / 年

代金券:在阿里云专用满减优惠券

正文完
星哥说事-微信公众号
post-qrcode
 0
星锅
版权声明:本站原创文章,由 星锅 于2024-07-24发表,共计4628字。
转载说明:除特殊说明外本站文章皆由CC-4.0协议发布,转载请注明出处。
【腾讯云】推广者专属福利,新客户无门槛领取总价值高达2860元代金券,每种代金券限量500张,先到先得。
阿里云-最新活动爆款每日限量供应
评论(没有评论)
验证码
【腾讯云】云服务器、云数据库、COS、CDN、短信等云产品特惠热卖中