关闭

Nginx 失败重试机制

时间: 2019-10-04阅读: 314标签: Nginx

背景

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

本文中的结论在以下环境进行验证:

  • 版本详情:nginx/1.16.0

  • 安装方式:使用 apt 从 nginx 官方源安装


如何定义 fails

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

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

fails 可以分成两类:

  1. 默认错误,包括 error、timeout

  2. 选择定义错误,包含 invalid_header 以及各种异常 http 状态码错误等

默认错误

关于 默认错误 ,我们再详细解析一下这两种错误。关于这两种错误的定义,官网文档已经描述的非常清楚了:

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

出现 error 的场景,常见的是上游服务器的服务重启、停止,或者异常崩溃导致的无法提供正常服务。而 timeout 的情况,就是代理请求过程中达到对应的超时配置,主要包括了:

  • proxy_connect_timeout ,建立三次握手的时间

  • proxy_read_timeout ,建立连接后,等待上游服务器响应以及处理请求的时间

  • proxy_send_timeout ,数据回传的间隔时间(注意不是数据发送耗时)

选择定义错误

关于 选择定义错误 ,异常状态码部分(也就是 4xx、5xx 错误)应该是比较好理解,这里主要说一下 invalid_header

invalid_header: a server returned an empty or invalid response;

这个场景就是上游服务器返回空响应或者非法响应头,包括且不仅限于:

  • 上游服务器的业务使用了非标准的 HTTP 协议,nginx 校验不通过

  • 因服务异常导致响应请求处理返回了异常 header(或者空 header)

NOTE

默认只有 error、timeout 会被认为是 fails,统计到健康检测的 max_fails 计数,如果通过 proxy_next_upstream 定义了其他类型的 fails,那这部分 fails 也会被算到计数器。

在选择自定义错误的配置上,一定要十分慎重,必须要结合业务实际情况来调整配置,而不是直接复制网上或者其他站点的配置,否则可能踩坑:

  • 配置了不合理的错误类型,可能导致一些非预期的所有节点被踢掉的情况

  • 缺少对关键错误类型的定义,导致出问题的节点一直没有被踢掉,影响客户端访问


重试机制解析

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 两个 Server 处理。假设这时 A 节点服务崩溃,端口不通,则会出现这种情况:

  1. 请求 1 转到 A 异常,再重试到 B 正常处理,A fails +1

  2. 请求 2 转到 B 正常处理

  3. 请求 3 转到 A 异常,再重试到 B 正常处理,A fails +1 达到 max_fails 将被屏蔽 60s

  4. 屏蔽 A 的期间请求都只转给 B 处理,直到屏蔽到期后将 A 恢复重新加入存活列表,再按照这个逻辑执行

如果在 A 的屏蔽期还没结束时,B 节点的服务也崩溃,端口不通,则会出现:

  1. 请求 1 转到 B 异常,此时所有线上节点异常,会出现:

    • AB 节点一次性恢复,都重新加入存活列表

    • 请求转到 A 处理异常,再转到 B 处理异常

    • 触发  no live upstreams 报错,返回 502 错误

    • 所有节点再次一次性恢复,加入存活列表

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

重试限制方式

默认配置是没有做重试机制进行限制的,也就是会尽可能去重试直至失败。

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

  • proxy_next_upstream_tries :设置重试次数,默认 0 表示无限制,该参数包含所有请求 upstream server 的次数,包括第一次后之后所有重试之和;

  • proxy_next_upstream_timeout :设置重试最大超时时间,默认 0 表示不限制,该参数指的是第一次连接时间加上后续重试连接时间,不包含连接上节点之后的处理时间

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

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 端口的请求来模拟 connect timeout 的情况:

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

则具体的请求处理情况如下:

  1. 请求 1 到达 Nginx,按照以下逻辑处理

    • 先转到 A 处理,3s 后连接超时,A fails +1

    • 重试到 B 处理,3s 后连接超时,B fails +1

    • 到达设置的 6s 重试超时,直接返回 `504 Gateway Time-out` 到客户端,不会重试到 C

  2. 请求 2 转到 C 正常处理

  3. 请求 3 到达 Nginx

    • 先转到 B 处理,3s 后连接超时,B 达到 max_fails 将被屏蔽 60s

    • 转到 C 正常处理

  4. 请求 4 达到 Nginx:

    • 先转到 A 处理,3s 后连接超时,A 达到 max_fails 将被屏蔽 60s

    • 转到 C 正常处理

  5. 后续的请求将全部转到 C 处理直到 AB 屏蔽到期后重新加入服务器存活列表

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

重试次数的情况也是类似,这里就不展开细讲了。

关于 backup 服务器

Nginx 支持设置备用节点,当所有线上节点都异常时启用备用节点,同时备用节点也会影响到失败重试的逻辑,因此单独列出来介绍。

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

  1. 正常情况下,请求不会转到到 backup 服务器,包括失败重试的场景

  2. 当所有正常节点全部不可用时,backup 服务器生效,开始处理请求

  3. 一旦有正常节点恢复,就使用已经恢复的正常节点

  4. backup 服务器生效期间, 不会存在所有正常节点一次性恢复的逻辑

  5. 如果全部 backup 服务器也异常,则会将所有节点一次性恢复,加入存活列表

  6. 如果全部节点(包括 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 异常的情况下,与上文没有 backup 服务器场景处理方式一致,这里就不重复介绍了。

假设在 A 的屏蔽期还没结束时,B 节点的服务也崩溃,端口不通,则会出现:

  1. 请求 1 转到 B 处理,异常,此时所有线上节点异常,会出现:

    • AB 节点一次性恢复,都重新加入存活列表

    • 请求转到 A 处理异常,再重试到 B 处理异常,两者 fails 都 +1

    • 因 AB 都异常,启用 backup 节点正常处理,并且 AB 节点一次性恢复,加入存活列表

  2. 请求 2 再依次经过 A、B 节点异常,转到 backup 处理,两者 fails 都达到 max_fails:

    • AB 节点都将会被屏蔽 60s,并且不会一次性恢复

    • backup 节点正式生效,接下来所有请求直接转到 backup 处理

    • 直到 AB 节点的屏蔽到期后,重新加入存活列表

假设 AB 的屏蔽期都还没结束时,C 节点的服务也崩溃,端口不通,则会出现

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

    • ABC 三个节点一次性恢复,加入存活列表

    • 请求转到 A 处理异常,重试到 B 处理异常,最后重试到 C 处理异常

    • 触发 `no live upstreams` 报错,返回 502 错误

    • 所有节点再次一次性恢复,加入存活列表

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


踩坑集锦

如果不熟悉 HTTP 协议,以及 Nginx 的重试机制,很可能在使用过程中踩了各种各样的坑:

  • 部分上游服务器出现异常却没有重试

  • 一些订单创建接口,客户端只发了一次请求,后台却创建了多个订单,等等…

以下整理了一些常见的坑,以及应对策略。

需要重试却没有生效

接口的 POST 请求允许重试,但实际使用中却没有出现重试,直接报错。

从 1.9.13 版本,Nginx 不再会对一个非幂等的请求进行重试。如有需要,必须在 proxy_next_upstream 配置项中显式指定 non_idempotent 配置。参考 RFC-2616 的定义:

  • 幂等 HTTP 方法:GET、HEAD、PUT、DELETE、OPTIONS、TRACE

  • 非幂等 HTTP 方法:POST、LOCK、PATCH

如需要允许非幂等请求重试,配置参考如下(追加 non_idemponent 参数项):

proxy_next_upstream error timeout non_idemponent;

该配置需要注意的点:

  1. 添加非幂等请求重试是追加参数值,不要把原来默认的 error/timeout 参数值去掉

  2. 必须明确自己的业务允许非幂等请求重试以避免业务异常

禁止重试的场景

一些场景不希望请求在多个上游进行重试,即使上游服务器完全挂掉。

正常情况下,Nginx 会对 error、timeout 的失败进行重试,对应默认配置如下:

proxy_next_upstream error timeout;

如希望完全禁止重试, 需要显式指定配置来关闭重试机制 ,配置如下:

proxy_next_upstream off;

重试导致性能问题

错误配置了重试参数导致 Nginx 代理性能出现异常

默认的 error/timeout 是不会出现这种问题的。在定义重试场景时,需要结合业务情况来确定是否启用自定义错误重试,而不是单纯去复制其他服务的配置。比如对于某个业务,没有明确自己业务情况,去网上复制了 Nginx 配置,其中包括了:

proxy_next_upstream error timeout invalid_header http_500 http_503 http_404;

那么只需要随便定义一个不存在的 URI 去访问该服务频繁去请求该服务,就可以重复触发 Nginx no live upstreams 报错,这在业务高峰情况下,性能将受到极大影响。同样,如果使用的代码框架存在不标准 HTTP 处理响应情况,恶意构造的请求同样也会造成类似效果。

因此在配置重试机制时, 必须先对业务的实际情况进行分析,严谨选择重试场景。

异常的响应超时重试

某幂等接口处理请求耗时较长,出现非预期的重试导致一个请求被多次响应处理。

假设该接口处理请求平均需要 30s,而对应的代理超时为:

proxy_read_timeout 30s;

默认的重试包含了 timeout 场景,在这个场景下,可能会有不到一半的请求出现超时情况,同时又因为是幂等请求,所有会进行重试,最终导致一个的超时请求会被发到所有节点处理的请求放大情况。

因此在进行超时设置时,也必须要跟进业务实际情况来调整。可以适当调大超时设置,并收集请求相关耗时情况进行统计分析来确定合理的超时时间。

异常的连接超时重试

因上游服务器异常导致连接问题,客户端无超时机制,导致请求耗时非常久之后才失败。

已知所有上游服务器异常,无法连接或需要非常久(超过 10s)才能连接上,假设配置了连接超时为:

proxy_connect_timeout 10;

在这种情况下,因客户端无超时设置,幂等请求将卡住 10*n 秒后超时(n 为上游服务器数量)。

因此建议:

  1. 客户端设置请求超时时间

  2. 配置合理的 proxy_connect_timeout

  3. 配合 proxy_next_upstream_timeout 、 proxy_next_upstream_tries 来避免重试导致更长超时


站长推荐

1.云服务推荐: 国内主流云服务商,各类云产品的最新活动,优惠券领取。地址:阿里云腾讯云华为云

2.广告联盟: 整理了目前主流的广告联盟平台,如果你有流量,可以作为参考选择适合你的平台点击进入

链接: http://www.fly63.com/article/detial/8508

关闭

nginx的一般安全配置

项目一般配置,例如default.conf;nginx的主入口nginx.conf;lua的一些应用一般爬虫无法动态cookie,用作判断是否是浏览器行为;thinkphp.conf相关的配置

Nginx服务器 之反向代理与负载均衡

客户端就可以通过请求代理服务器,获取想要的资源,但客户端并不知道给他资源的是哪个服务器。这种方式就是反向代理。当一台服务器的单位时间内的访问量越大的时候,服务器的压力会越大。我们通常通过负载均衡的方式来分担服务器的压力。

nginx去掉url中的index.php

使用情境:我想输入www.abc.com/a/1后,实际上是跳转到www.abc.com/index.php/a/1,配置Nginx.conf在你的虚拟主机下添加:如果你的项目入口文件在一个子目录内,则.

Nginx解析PHP的原理 | CGI、FastCGI及php-fpm的关系

php-fpm采用master/worker架构设计, master进程负责CGI、PHP公共环境的初始化及事件监听操作。worker进程负责请求的处理功能。在worker进程处理请求时,无需再次初始化PHP运行环境,这也是php-fpm性能优异的原因之一

Nginx 负载均衡

对于电商平台而言,随着业务的不断发展壮大,网站访问量和数据量也随之急剧增长,该情况的产生给服务器带来了一定的负担。从用户体验层面而言,由于服务器端数据处理带来的时延,往往导致页面的响应速度过慢

nginx http内核模块提供的变量和解释

ngx_http_core_module模块在处理请求时,会有大量的变量,这些变量可以通过访问日志来记录下来,也可以用于其它nginx模块 。顺便对ngx_http_core_module模块提供的变量总结了下,如下所示:

php环境下nginx超时问题解决

nginx访问出现504 Gateway Time-out,一般是由于程序执行时间过长导致响应超时,例如程序执行需要90秒,而nginx最大响应等待时间为30秒,这样就会出现超时。通常有以下几种情况导致:

总结nginx中的location配置

Location指令是nginx中最关键的指令之一,location指令的功能是用来匹配不同的url请求,进而对请求做不同的处理和响应,这其中较难理解的是多个location的匹配顺序,本文会作为重点来解释和说明。

Nginx除了负载均衡,还可以做什么?

Nginx应该是现在最火的web和反向代理服务器,没有之一。她是一款诞生于俄罗斯的高性能web服务器,尤其在高并发情况下,相较Apache,有优异的表现。那除了负载均衡,她还有什么其他的用途呢,下面我们来看下。

nginx做http向https的自动跳转

首先让nginx服务器监听两个端口,分别是80端口和443端口,注意监听443端口的时候需要配置证书的认证以及创建自签名证书!关于证书的认证的以及创建自签名的证书,nginx的配置如下,只给出了两个server的配置,可以直接复制到http块中。

点击更多...

内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!