2022
我们一起努力

Nginx 失败重试机制

来源

网易游戏SRE,喜欢学习分享。后台

Nginx作为一种广泛使用的反向代理服务,原生提供了一套失败重试机制来保证服务的可用性。 本文主要通过一些简单的例子来分析Nginx的失败重试机制,让读者对该机制有一个基本的了解,避免在使用过程中踩坑。

本文的结论在以下环境中得到验证:

如何定义失败

在了解Nginx的失败重试机制之前,需要了解Nginx是如何定义失败的。

Nginx通过proxy_next_upstream参数来定义在什么情况下会被认为是失败,从而触发失败重试机制。

失败可以分为两类:

默认错误,包括错误、超时

Select定义错误,包括invalid_header和各种异常http状态码错误等。

默认错误

关于默认错误,我们来详细分析一下这两个错误。 关于这两个错误的定义,官网文档已经描述的很清楚了:

错误:与服务器建立连接、向其传递请求或读取响应标头时发生错误

超时:与服务器建立连接、向其传递请求或读取响应标头时发生超时

在出现错误的情况下,上游服务器的服务重启、停止或异常崩溃而无法提供正常服务是很常见的。 在超时的情况下,代理请求过程中达到相应的超时配置,主要包括:

选择定义错误

关于选择定义错误,异常状态码部分(即4xx、5xx错误)应该比较容易理解。 这里主要讲invalid_header

invalid_header:服务器返回了一个空的或无效的响应;

这种场景是上游服务器返回一个空的响应或者一个无效的响应头,包括但不限于:

笔记

默认情况下,只有error和timeout会被认为是fail,健康检查的max_fails次数会被统计。 如果通过proxy_next_upstream定义了其他类型的fail,那么这部分fail也会被计入计数器。

选择自定义错误配置时必须非常谨慎。 一定要根据实际业务情况调整配置,不能直接照搬网上或者其他网站的配置,否则可能会踩坑:

重试机制分析

Nginx的失败重试是为了实现对客户端透明的服务端高可用。 不过这部分失败重试机制比较复杂,官方文档没有详细介绍。 本文将对其进行分析,并结合实际场景的例子使其更容易理解。

基本失败重试

本节介绍最常见和最基本的失败重试场景。

为了方便理解,采用如下配置进行分析(proxy_next_upstream无特殊配置):

upstream test {
    server 127.0.0.1:8001 fail_timeout=60s max_fails=2# Server A
    server 127.0.0.1:8002 fail_timeout=60s max_fails=2# Server B

模拟后端异常的方式是直接关闭相应的服务连接到任意官方服务器失败连接到任意官方服务器失败,导致connect refused的情况,对应error错误。

初始阶段,所有服务器都正常,请求会按照轮询的方式依次转发给AB的两台服务器。 假设此时A节点服务崩溃,端口被阻塞,就会出现这样的情况:

请求1去A异常,然后重试,直到B处理正常,A失败+1

请求2转到B进行正常处理

请求3异常到A去,然后重试,直到B处理正常,A失败+1达到max_fails会阻塞60s

屏蔽A期间,请求只转给B处理,直到屏蔽期满,恢复A重新加入生存列表,再按此逻辑执行

如果B节点的服务也崩溃了,A的屏蔽期还没有结束,端口就被阻塞了,就会出现:

请求1去B异常,此时所有在线节点都异常,会出现:

请求2依次经过AB后无法正常处理,触发no live upstreams错误,返回502错误

重试限制方法

默认配置不受重试机制的限制,即会尽可能重试,直到失败为止。

Nginx 提供了以下两个参数来控制重试次数和重试超时时间:

为了便于理解,使用以下配置进行说明(仅列出关键配置):

proxy_connect_timeout 3s;
proxy_next_upstream_timeout 6s;
proxy_next_upstream_tries 3;

upstream test {
    server 127.0.0.1:8001 fail_timeout=60s max_fails=2# Server A
    server 127.0.0.1:8002 fail_timeout=60s max_fails=2# Server B
    server 127.0.0.1:8003 fail_timeout=60s max_fails=2# Server C
}

第2~3行表示6秒内允许重试3次。 只要超过其中任何一个设置,Nginx 就会结束重试并返回客户端响应(可能是错误代码)。 我们使用 iptables DROP 将请求丢弃到 8001 和 8002 端口来模拟连接超时的情况:

iptables -I INPUT  -p tcp -m tcp --dport 8001 -j DROP
iptables -I INPUT  -p tcp -m tcp --dport 8002 -j DROP

具体请求处理如下:

请求1到达Nginx,按照如下逻辑进行处理

请求 2 去 C 正常处理

请求 3 到达 Nginx

请求 4 命中 Nginx:

后续的请求都会转给C处理,直到AB盾失效重新加入服务器生存列表

从上面的例子我们可以看出proxy_next_upstream_timeout配置项对重试机制的限制,

重试次数的情况类似,这里不再赘述。

关于备份服务器

Nginx 支持备份节点的设置。 当所有在线节点都出现异常时,启用备份节点。 同时备份节点也会影响失败重试的逻辑,所以单独列出来。

在upstream的配置中,可以通过backup命令定义备服务器,其含义如下:

一般情况下,请求是不会去备份服务器的,包括失败重试的场景

当所有正常节点都不可用时,备份服务器生效并开始处理请求

一旦正常节点恢复,使用恢复的正常节点

备份服务器生效期间,不会有一次性恢复所有正常节点的逻辑

如果所有备份服务器也异常,则一次性恢复所有节点,加入生存列表

如果所有节点(包括backup)都异常,Nginx返回502错误

为了便于理解,采用如下配置进行说明:

upstream test {
    server 127.0.0.1:8001 fail_timeout=60s max_fails=2# Server A
    server 127.0.0.1:8002 fail_timeout=60s max_fails=2# Server B
    server 127.0.0.1:8003 backup; # Server C

初始阶段,所有服务器都正常,请求会按照轮询的方式依次转发给AB的两个节点。 当只有A异常时,处理方法同上述没有备份服务器的场景,这里不再赘述。

假设在A的屏蔽期结束前,B节点的服务也崩溃了,端口不可达,会出现如下:

请求1转给B处理,出现异常。 此时所有在线节点都出现异常,会出现:

Request 2依次经过节点A和B的异常,转为backup处理,均失败达到max_fails:

假设AB的屏蔽期还没有结束,C节点的服务也崩溃了,端口不可达,就会出现

请求1去C异常,此时所有节点(包括backup)都异常,会出现:

请求2依次经过AB节点异常,重试到C异常,最终结果同上一步,返回502错误

踩坑亮点

赞(0)
文章名称:《Nginx 失败重试机制》
文章链接:https://www.fzvps.com/56712.html
本站文章来源于互联网,如有侵权,请联系管理删除,本站资源仅供个人学习交流,请于下载后24小时内删除,不允许用于商业用途,否则法律问题自行承担。
图片版权归属各自创作者所有,图片水印出于防止被无耻之徒盗取劳动成果的目的。

评论 抢沙发

评论前必须登录!