结论
经过多次模拟线上的环境测试,Nginx 负载均衡技术默认情况下已经对于 connect refused(状态码表现为 502)和 time out(状态码表现为 504)做了失效转移,使用的是 upstream 模块的 proxy_next_upstream 指令(这个选项默认是启动的)来实现。
对于 http GET 请求,当这个请求转发到上游服务器发生断路,或者读取响应超时则会将同样的请求转发到其他上游服务器来处理,如果所有服务器都超时或者断路,则会返回 502 或者 504 错误。
对于http POST 请求,当这个请求转发到上游服务器发生断路,则会将请求转发到其他上游服务器来处理,但是如果这个请求发生了读取超时,则不会做失效转移,会返回 504 错误,Nginx 之所以这么做应该是为了防止同一个请求发送两次,比如涉及到银行的充值等操作就会发生很严重的 bug。以下是模拟线上的场景测试得出的结论:
- 上游服务器有两台,一台处于 down 状态,另一台处于正常服务状态,那么来自客户端的
GET和POST请求都会通过 Nginx 的失效转移机制路由到正常状态的机器,返回200状态码,并不会返回给客户端502错误; - 上游服务器有两台,两台都 down 了,那么会不管是
GET还是POST请求都会直接返回给客户端502错误; - 上游服务器有两台,一台机器的
http GET和POST接口都正常 return,另一台相同的接口死循环,模拟超时。
这种情况下如果客户端的请求路由到了正常机器,那么直接返回200。
如果请求路由到了死循环的接口,并且是GET请求,那么会等待 Nginx 设置的超时时间过后,然后将请求转发到另一台机器的正常接口。
如果请求路由到了死循环的接口,并且是POST请求,那么等待 nginx 设置的超时时间过后直接返回504,没有进行失效转移,防止请求的重复发送; - 上游服务器有两台,两台机器的
http GET和POST接口都死循环,模拟超时,那么对于GET请求会进行请求转发到另一台尝试,对于POST请求直接返回504,不会进行进一步尝试;
论证环境及工具
- 一台前端 Nginx 服务器;
- 两台上游服务器;
- Nginx 配置:
1 | server { |
- 第一台上游服务器正常代码
1 | import time |
- 第二台上游服务器超时代码
1 | import time |
论证过程
以下为前面 4 种案例的论证过程:
案例 1
上游服务器有两台,一台处于 down 状态,另一台处于正常服务状态。
在这种情况下,通过 curl 多次发送 GET 和 POST 请求,发现不管怎么请求,返回都是正常状态,如果 Nginx 发生了失败尝试操作,那么会在 Nginx access 日志中的 upstream 字段看到有两个服务器的地址。
发送 GET 和 POST 请求:
1 | curl -XGET https://ngxfailover.xxx.me/a/hello |
观察日志:
可以看出所有请求都成功了,红框框圈起来的请求表示发生了失效转移,并且请求成功。
案例 2
上游服务器有两台,两台服务器都处于 down 状态。
在这种情况下不管是 GET 还是 POST 请求都会直接返回给客户端 502 错误。
发送 GET 和 POST 请求:
1 | curl -XGET https://ngxfailover.xxx.me/a/hello |
观察日志:
可以看出所有请求全部返回 502 错误,红框框圈起来的请求表示发生了失效转移,但是还是失败了。
案例 3
上游服务器有两台,一台机器的 http GET 和 POST 接口都正常 return,另一台相同的接口死循环,模拟超时。
这种情况下如果客户端的请求路由到了正常机器,那么直接返回 200。
如果请求路由到了死循环的接口,并且是 GET 请求,那么会等待 Nginx 设置的超时时间过后,然后将请求转发到另一台机器的正常接口。
如果请求路由到了死循环的接口,并且是 POST 请求,那么等待 Nginx 设置的超时时间过后直接返回客户端 504 错误,没有进行失效转移,防止请求的重复发送。
发送 GET 请求:
1 | curl -XGET https://ngxfailover.xxx.me/a/hello |
观察日志:
可以看到对于 GET 请求全部成功,红框框圈起来的表示发生了失效转移,第一台超时后会是继续尝试第二台,最终成功。
发送 POST 请求:
1 | curl -XPOST https://ngxfailover.xxx.me/b/hello |
观察日志:
可以看到对于 POST 请求,如果 Nginx 等待上游服务器处理请求超时,并不会发生失效转移,直接返回给客户端 504 错误。
案例 4
上游服务器有两台,两台机器的 http GET 和 POST 接口都死循环,模拟超时。
这种情况下对于 GET 请求会将请求转发到另一台尝试,对于 POST 请求直接返回 504 错误,不会进行进一步尝试。
发送 GET 请求:
1 | curl -XGET https://ngxfailover.xxx.me/a/hello |
观察日志:
可以看出对于 GET 请求,Nginx 在等待超时会继续进行尝试,两台都尝试失败后返回了 504 错误。
发送 POST 请求:
1 | curl -XPOST https://ngxfailover.xxx.me/b/hello |
观察日志:
可以看出对于 POST 请求,Nginx 在等待超时会不继续进行尝试其他上游服务器,直接返回 504 错误。
总结
总体来看 Nginx 的失效转移技术已经非常成熟,Nginx 默认情况下对于 connect refused(状态码表现为 502)和 time out(状态码表现为 504)已经做了失效转移,并且 Nginx 根据请求的类型不同,对失效转移的策略也不同。对于服务器后台状态没有改变的请求(比如 GET 请求)会进行失效转移,对于服务后台状态有改变的请求(比如 POST 请求),有失效转移机制,这也符合 Rest API 的冪等性标准。如果要强行加其他状态码的失效转移,比如 500、503 等,需要考量下业务请求是否能容忍请求的重复发送。