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

Nginx 健康检查和负载均衡机制分析

165次阅读
没有评论

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

nginx 是优秀的反向代理服务器,这里主要讲它的健康检查和负载均衡机制,以及这种机制带来的问题。所谓健康检查,就是当后端出现问题(具体什么叫出现问题,依赖于具体实现,各个实现定义不一样),不再往这个后端分发请求,并且做后续的检查,直到这个后端恢复正常。所谓负载均衡,就是选择后端的方式,如何(根据后端的能力)将请求均衡的分发到后端。此外,当请求某个后端失败时,要将该请求分发到其它后端(Redispatch)。这里以 ngx_http_upstream_round_robin(简称 RR)做为负载均衡模块,以 ngx_http_proxy_module(检查 proxy)作为后端代理模块。

nginx 的健康检查和负载均衡是密切相关的,它没有独立的健康检查模块,而是使用业务请求作为健康检查,这省去了独立健康检查线程,这是好处。坏处是,当业务复杂时,可能出现误判,例如后端响应超时,这是可能是后端宕机,也可能是某个业务请求自身出现问题,跟后端无关。如果后端宕机,nginx 还要在将它标记为不可用之后,仍不时的将业务请求分发给它,以检查后端是否恢复。

nginx 完成客户端请求 Header 部分的解析,upstream 调用 RR 模块的 peer.get 选择具体的后端。当请求结束,upstream 调用 RR 模块的 peer.free,向 RR 反馈后端的健康情况。当 upstream 和后端通信时,出现错误会调用 ngx_http_upstream_next,

void ngx_http_upstream_next(ngx_http_request_t *r, ngx_http_upstream_t *u, ngx_uint_t ft_type); 第三个参数指明错误类型,共有如下错误类型

#define NGX_HTTP_UPSTREAM_FT_ERROR          0x00000002

#define NGX_HTTP_UPSTREAM_FT_TIMEOUT        0x00000004

#define NGX_HTTP_UPSTREAM_FT_INVALID_HEADER  0x00000008

#define NGX_HTTP_UPSTREAM_FT_HTTP_500        0x00000010

#define NGX_HTTP_UPSTREAM_FT_HTTP_502        0x00000020

#define NGX_HTTP_UPSTREAM_FT_HTTP_503        0x00000040

#define NGX_HTTP_UPSTREAM_FT_HTTP_504        0x00000080

#define NGX_HTTP_UPSTREAM_FT_HTTP_404        0x00000100

#define NGX_HTTP_UPSTREAM_FT_UPDATING        0x00000200

#define NGX_HTTP_UPSTREAM_FT_BUSY_LOCK      0x00000400

#define NGX_HTTP_UPSTREAM_FT_MAX_WAITING    0x00000800

#define NGX_HTTP_UPSTREAM_FT_NOLIVE          0x40000000

ngx_http_upstream_next,只要错误类型不是 NGX_HTTP_UPSTREAM_FT_HTTP_404,都认为后端有问题(NGX_PEER_FAILED)

if (ft_type == NGX_HTTP_UPSTREAM_FT_HTTP_404) {

  state = NGX_PEER_NEXT;                                                                                 

} else {

  state = NGX_PEER_FAILED;                                                                               

}

ngx_http_upstream_next 调用 RR 的 peer.free,RR 根据 state 判断刚才接受请求的后端是否健康。

if (ft_type != NGX_HTTP_UPSTREAM_FT_NOLIVE) {

  u->peer.free(&u->peer, u->peer.data, state);

}

ngx_http_upstream_next 如果超过最大重试次数(默认为后端的个数,每试过一个,就减 1),或者 proxy 设置不允许 redispatch,则向客户端返回响应 status。

if (u->peer.tries == 0 || !(u->conf->next_upstream & ft_type)) {

  ngx_http_upstream_finalize_request(r, u, status);

}

proxy 模块的 proxy_next_upstream 配置,在何种情况下将请求 redispatch 到下一个后端。

刚刚谈到,只要错误类型不是 NGX_HTTP_UPSTREAM_FT_HTTP_404,都认为后端有问题。这里的错误类型包括,连接后端失败,连接,读写后端超时,后端返回了 500,502,504 等。这个策略是有待商榷的,尤其是读写后端超时也判断为后端不可用。因为某个业务请求,可能因为自身的原因而导致读写超时。注意,在 proxy_next_upstream 中指定 timeout,http_504 是不同的,前者表示 upstream 连接,读写后端超时,后者表示后端返回的 http code 是 504。

实际上健康检查不是必须的,因为 redispatch 的存在保证了,就算有后端宕机,客户端仍将收到正确的响应。那么我们考虑关掉健康检查。通过 upstream 的 server 配置的 max_fails 参数

RR 的 peer.get,如果 max_fails 为 0,则该后端总是可用的(就算它真有问题)。

if (peer->max_fails == 0

  || peer->fails < peer->max_fails)

{

  break;

}

因为 redispatch 的次数,取决于后端的个数,所以后端的个数稍微多一点是有好处的。

下面是一些佐证分析的测试。

upstream test {

  server 127.0.0.1:8060 max_fails=0;

  server 127.0.0.1:8070 max_fails=0;

  server 127.0.0.1:8080 max_fails=0;

  server 127.0.0.1:8090 max_fails=0;

}

只有 8060,8070 是存活的,8080,8090 处于不可用状态,这里 max_fails=0,关闭了健康检查。

proxy_read_timeout 2;

读超时设为 2S。

proxy_next_upstream error timeout;

默认当 error 和 timeout 发生时,redispatch。

测试请求的 sleep 参数指定后端的 sleep 时间,code 参数指定后端返回的 http code。根据 time 和 sleep 时间的对比,判断重试了几个后端。

time curl “http://127.0.0.1:8099/index.php?sleep=3” -vv

real    0m4.014s

sleep=3,读超时,重试了 2 个后端。

修改配置 proxy_next_upstream error;

time curl “http://127.0.0.1:8099/index.php?sleep=3” -vv

real    0m2.018s

读超时,不再 redispatch,重试了 1 个后端。

修改配置 proxy_next_upstream error http_504;

time curl “http://127.0.0.1:8099/index.php?sleep=1” -vv

real    0m1.022s

这个是正常请求。

time curl “http://127.0.0.1:8099/index.php?sleep=1&code=504” -vv

real    0m2.023s

让后端返回 504,此时 nginx 会做 redispatch,重试了 2 个后端

但是 nginx 返回给客户端的是 502,不是 504,因为所有的后端都返回 504,nginx 认为后端不可用,返回 502.

测试健康检查,关掉 redispatch。proxy_next_upstream off;

curl “http://127.0.0.1:8099/index.php?sleep=3” -vv

返回了两次 502,两次 504。存活的后端返回 504,有问题的返回 502。

修改 max_fails server 127.0.0.1:8060 max_fails=1; 对 8060 开启健康检查。

curl “http://127.0.0.1:8099/index.php?sleep=3” -vv

第一轮 4 次请求,返回两次 502,两次 504

8080 和 8090 有问题,返回 502,8060 和 8070 响应超时,返回 504,因为 8060 开启了健康检查,并且返回了 504,所以被标记为不可用。

第二轮 4 次请求,返回三次 502,一次 504。8070 没有开启健康检查,所以仍然返回 504。

根据测试分析,业务请求(sleep 3s,或者 输出 http 504)可以让 nginx 误以为后端宕了,而这时后端活得好好的。在私有云平台,这个通常不是问题,把超时设大点,不返回 5XX 错误,可以避免这个问题。但是在公有云平台,这是致命的,因为业务可以编程输出 5XX 错误。有两种方法应对,一种是关闭健康检查,一种是修改 nginx 的代码,仅对 NGX_HTTP_UPSTREAM_FT_ERROR 判定为后端有问题。

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