nginx作为反向代理服务器,后端有多台服务器,上层通过一定机制保证容错和负载均衡。

nginx的重试机制就是容错的一种

官方链接:http://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_next_upstream

Syntax:

proxy_next_upstream error | timeout | invalid_header | http_500 | http_502 | http_503 | http_504 | http_403 | http_404 | http_429 | non_idempotent | off

Default:

proxy_next_upstream error timeout;

Context:

httpserverlocation

指定在哪种情况下将请求传递到下一个服务器:

error

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

timeout

与服务器建立连接,向服务器传递请求或读取响应头时发生超时;

invalid_header

服务器返回空的或无效的响应;

http_500

服务器返回代码为500的响应;

http_502

服务器返回代码为502的响应;

http_503

服务器返回代码为503的响应;

http_504

服务器返回代码为504的响应;

http_403

服务器返回代码为403的响应;

http_404

服务器返回代码为404的响应;

http_429

服务器返回代码为429的响应;(1.11.13);

non_idempotent

通常,具有请求 非幂等 方法(POST,LOCK,PATCH)不传递到请求是否已被发送到上游服务器(1.9.13)的下一个服务器;

 启用此选项将允许重试此类请求;

off

禁用将请求传递到下一个服务器。

  • 应该记住的是,只有在还没有任何内容发送给客户端的情况下,才有可能将请求传递给下一台服务器。即,如果在响应的传输过程中发生错误或超时,则无法解决该问题。
  • 该指令还定义了与服务器通信的不成功尝试 的情况下。错误、超时和invalid_header的情况始终被视为不成功的尝试,即使在该指令中没有指定他们。只有在指令中指定http_500、http_502、http_503、http_504和http_429的情况下才被视为不成功的尝试。http_403和http_404 永远不会被视为不成功的尝试。

下面还有一个参数影响重试次数,0表示不限制。

Syntax:     proxy_next_upstream_tries number;

Default:    proxy_next_upstream_tries 0;

Context:    http, server, location

#该配置决定了最多重试多少次,0表示不限制。

配置示例:

upstream app-proxy {
    server 192.168.5.100:8080;
    server 192.168.5.101:8080;
    check interval=2000 rise=1 fall=3 timeout=3000 type=http;
    check_keepalive_requests 1;
#    check_http_send "HEAD /status/status.html HTTP/1.1\r\n\r\n"; 
    check_http_send "GET /status/status.html HTTP/1.1\r\nConnection: close\r\nHost: localhost\r\n\r\n"; 
    check_http_expect_alive http_2xx http_3xx;
}
location / {
      proxy_pass         http://app-proxy;
      proxy_next_upstream error timeout http_500 http_502 http_503 http_504;
      proxy_next_upstream_tries 3;
      proxy_connect_timeout 60s;
      proxy_read_timeout 60s;
      proxy_send_timeout 60s;
      proxy_pass_request_headers      on;
      proxy_set_header   Host             $host:$server_port;
      proxy_set_header   X-Real-IP        $remote_addr;
      proxy_set_header   X-Forwarded-For  $proxy_add_x_forwarded_for;
      set $domain default;

Ps:

upstream fail_timeout可能造成的问题

upstream web_server {
        server 192.168.1.10:8000 weight=1 max_fails=3 fail_timeout=10s;
        server 192.168.1.10:8000 weight=1 max_fails=3 fail_timeout=10s;
        keepalive 2000;
}

通过配置上游服务器的max_fails 和fail_timeout,来指定每个上游服务器,当fail_timeout时间内失败了max_fails次请求,则认为该上游服务器不可用/不存活,然后会摘掉该上游服务器,fail_timeout时间后会再次将该服务器加入到存活上游服务器列表进行重试。

在nginx的配置文件中,proxy_next_upstream项定义了什么情况下进行重试,默认情况下,当请求服务器发生错误或超时时,会尝试到下一台服务器。

  • proxy_connect_timeout time:与后端/上游服务器建立连接的超时时间,默认为60s
  • proxy_read_timeout time:设置从后端/上游服务器读取响应的超时时间,默认为60s,此超时时间指的是两次成功读操作间隔时间,而不是读取整个响应体的超时时间,如果在此超时时间内上游服务器没有发送任何响应,则Nginx关闭此连接。
  • proxy_send_timeout time:设置往后端/上游服务器发送请求的超时时间,默认为60s,此超时时间指的是两次成功写操作间隔时间,而不是发送        整个请求的超时时间,如果在此超时时间内上游服务器没有接收任何响应,则Nginx关闭此连接。

考虑一个场景:

proxiy_read_timeout设置5s,当请求转发到服务器A,若处理时间超过5S(比如大文件上传),此时被nginx认定失败,转发请求到服务器B,这样A、B服务器将会重复处理数据。(当然,这种场景,内网可以通过机器名访问该服务器进行操作,就可以绕过nginx了,不过外网就没办法了。)

在处理POST请求的时候也需要注意类似的问题。网上有一篇讨论如何阻止POST请求的超时重试,感兴趣的可以看看。点击打开链接

将if指令(具有少数有效用例之一)与自定义错误处理程序结合使用:

upstream backend {
    server backend1;
    server backend2;
}

server {
    server_name www.qsh.com;

    location / {
        error_page 598 = @retry;
        error_page 599 = @no_retry;
        if ($request_method = POST) {
            return 599;
        }
        return 598;
    }

    location @retry {
        rewrite ^/api/(.*) /$1 break;
        add_header Method "GET";
        proxy_pass http://backend;
    }

    location @no_retry {
        rewrite ^/api/(.*) /$1 break;
        add_header Method "POST";
        proxy_pass http://backend;
        proxy_next_upstream off;
    }
}

总结:根据不同的业务场景考虑设置不同的超时时间