结论
经过多次模拟线上的环境测试,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
等,需要考量下业务请求是否能容忍请求的重复发送。