一般情况应用服务器的上游会部署一套nginx集群,用作反向代理、负载均衡,这个是很好的。
1. upstream容错重试
假设有五台应用服务器(Backend Server),其中两台宕机不可用;这时某nginx发送请求到其中一台(Backend Server),由于其不可用,Backend Server 会超时或返回错误状态码,然后直接返回给Client,这种场景的处理是很不友好的。
nginx中的upstream模块,不仅可以负载均衡,还能够对上面这种情况容错。
upstream可以通过判断节点失效状态、自动探测等方式,自动将问题server节点从访问列表中剔除,期间不会再将请求发送到该Server上(直到Server恢复),而将请求发送给下一台Server,如果下一台也有问题,继续往下重试直到所有Server被重试完。
1.1. 判断节点失效
nginx默认判断节点状态通过 connect refuse 和 time out 状态为准,不会以http状态码进行判断;它认为,只要能返回http状态码说明该节点还可以正常连接。
PS:如果是业务层面主动返回的500状态码,它就无法区分。
1.2. proxy_next_upstream指令
通过 proxy_next_upstream 指令,可以设置对相关http状态码的请求重试、容灾。
语法:
1 | proxy_next_upstream error | timeout | invalid_header | http_500 | http_502 | http_503 | http_504 | http_403 | http_404 | http_429 | non_idempotent | off ...; |
默认值:
1 | proxy_next_upstream error timeout; |
上下文环境:
1 | http, server, location |
参数含义:
参数 | 含义 |
---|---|
error | an error occurred while establishing a connection with the server, passing a request to it, or reading the response header; 在与服务器建立连接、向其传递请求或读取响应头时发生错误。 |
timeout | a timeout has occurred while establishing a connection with the server, passing a request to it, or reading the response header; 在与服务器建立连接、向其传递请求或读取响应头时发生超时。 |
invalid_header | a server returned an empty or invalid response; 服务器返回空的或无效的响应; |
http_500 | a server returned a response with the code 500; 后端服务器返回的响应状态码为500 |
http_502 | a server returned a response with the code 502; 后端服务器返回的响应状态码为502 |
http_503 | a server returned a response with the code 503; 后端服务器返回的响应状态码为503 |
http_504 | a server returned a response with the code 504; 后端服务器返回的响应状态码为504 |
http_403 | a server returned a response with the code 403; 后端服务器返回的响应状态码为403 |
http_404 | a server returned a response with the code 404; 后端服务器返回的响应状态码为404 |
http_429 | a server returned a response with the code 429 后端服务器返回的响应状态码为429 |
non_idempotent | normally, requests with a non-idempotent method (POST , LOCK , PATCH ) are not passed to the next server if a request has been sent to an upstream server (1.9.13); enabling this option explicitly allows retrying such requests;默认POST, LOCK, PATCH 这些会对服务器造成幂等的http请求,不会进行重试。一定要对这些http请求重试,则添加该参数。 |
off | disables passing a request to the next server. 停止将请求发送给下一台后端服务器 |
1.3. 超时参数
参数 | 含义 |
---|---|
proxy_connect_timeout | 与后端服务器建立连接的超时 默认值60s |
proxy_send_timeout | 向后端服务器传输请求的超时 默认值60s |
proxy_read_timeout | 从后端服务器读取响应的超时 默认值60s |
2. Nginx - upstream - 负载均衡策略
2.1. 普通轮询(round-robin)
默认策略,每个请求按照时间顺序逐一分配。
如果某个机器宕机(符合指定异常、状态码),跳过该机器向下一个机器发起重试。
1 | # 设定负载均衡服务器列表 |
2.2. 加权轮询
weight 指定轮询的权值,weight值越大,分配到的访问机率越高,该策略主要用于后端每个服务器性能不均的情况下。
1 | # 设定负载均衡服务器列表 |
2.3. ip_hash
按照Client Ip的hash结果分配;
这样来自同一个IP的client固定访问一个Backend Server,可以解决session不能跨服务器的问题。
当然如果这个节点不可用了,会发到下个节点,而此时没有session同步的话就注销掉了。
存在局限性,client IP一旦变化,hash值重新计算,可能就换backend server了。
1 | # 设定负载均衡服务器列表 |
2.4. least_conn
最少连接均衡;
请求被发送到当前活跃连接最少的后端服务器。会考虑weight的值。如果有多个后端服务器的 conns 值同为最小的,那么对它们采用加权轮询算法(weight)。
1 | upstream backend { |
2.5. sticky
Nginx的负载均衡-upstream模块,其负载均衡策略有普通轮询、加权轮询、ip_hash、least_conn等几种方式。
sticky也是负载均衡策略之一(需要引入相应模块),sticky是基于cookie实现负载的。
通过分发识别cookie,使得来自同一个客户端的请求落在同一台服务器上,默认cookie标识名为route。
2.5.1. sticky 原理
①: client 首次发起请求,nginx接收后请求头没有cookie,则以轮询的方式分发给Backend Server。
②: Backend Server业务处理后返回nginx。
③: 此时nginx生成cookie(route),返回client。route值与Backend Server相关联。
④: client接收请求并保存带route的cookie。
⑤: 当client下次发送请求时,cookie中就会带上route,nginx根据cookie中的route,将这个client的请求转发到固定的Backend Server。
2.5.2. 问题
sticky策略在某些场景大流量冲击下,会导致负载不均的情况。
当年大促,系统被流量冲垮后复盘,发现应用服务器流量不均衡;往上游排查时发现,系统负载的Nginx负载策略是 sticky,这个策略是中间件部门配置的默认参数,公司大部分系统,默认都是这个策略。
由于当时日志检索系统也存在问题、降级,导致没有采集到关键信息,比如nginx分配的cookie route。
最后整改方案,将sticky策略,修改成普通的轮询策略。
3. Reference
Using nginx as HTTP load balancer