笔者上次进行了GET请求的负载转发,默认是支持的。不需要额外的设置,这次再测试下POST请求。详细观察下文。
两个server端,还是A/B区域。
# server A端
import json
import uvicorn
import datetime
import asyncio
from fastapi import FastAPI, Request
from pydantic import BaseModel
class CountDemo(BaseModel):
count: int
app = FastAPI()
SERVER_NAME = "A"
@app.get("/get-test")
async def read_root():
return {
"Server": SERVER_NAME,
"Method": "GET",
"Time": datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
}
@app.post("/post-test")
async def read_root(count_demo: CountDemo):
return {
"Server": SERVER_NAME,
"Method": "Post",
"Time": datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
"Count": count_demo.count + 1
}
if __name__ == "__main__":
uvicorn.run("main:app", host="0.0.0.0", port=9090, log_level="info")
请求A端服务,响应A
请求B端服务,响应B
GET请求,调用接口还是雨露均沾。这里不配置负载策略,默认轮询。
Nginx日志打印的负载节点也是A/B分布
如果某个后端节点响应超时或连接超时,nginx会自动将请求转发给另一个节点。这里直接我们在服务A里面对每个接口都进行sleep操作,模拟一下超时响应。
客户端可以看到所有请求都正常转发,全给了B节点
Nginx日志可以看到发送给A节点的请求响应504了,后续自动转发给了B节点
如果客户端进行POST请求,Nginx在转发给节点A的时候504了,不会在转发给节点B
Nginx这样做的目的也是为了防止后端业务发生异常,因为POST是非幂等请求。如果转发给A节点等到超时又继续转发给B节点,就会出现A/B节点都执行同一个POST动作,而这个动作可能都会对数据库进行操作,造成业务问题。
如果Nginx配置,可以实现POST也继续转发给另一个节点的操作。
# 在location块内添加这个指令
proxy_next_upstream error timeout http_504 non_idempotent;
如果只认为配置 proxy_next_upstream error timeout http_504; 就能万事大吉,也不是,必须得加上 non_idempotent 整个POST转发才能生效。经过笔者测试,这几个参数是一整套,必须规定一个异常,可以是error,可以是timeout,或者具体的状态码,然后在配合 non_idempotent 参数进行强制转发。
蛋疼的是这几个参数的组合非常麻烦,笔者建议都配上。
proxy_next_upstream http_504 non_idempotent;
# 转发给B节点不生效
proxy_next_upstream error http_504 non_idempotent;
# 转发给B节点不生效
#
proxy_next_upstream timeout non_idempotent;
# 转发给B节点生效
网上的资料大部分也是都进行了配置
proxy_next_upstream error timeout http_504 non_idempotent;
这里值得注意的是,要配置就配置完善。否则连GET也会失效。
笔者测试:
假设A服务因为某些原因直接停调,而我们只配置了超时转发。proxy_next_upstream timeout non_idempotent;
也就是只有超时才进行转发,那这时候无论GET、POST都不会转发。笔者实测也是如此。
因此如果要对 proxy_next_upstream 指令进行配置,可以进行详细的配置。
没有特殊需求,笔者不建议配置 proxy_next_upstream 指令。
如果需要详细探究这个指令的内容,需要比较复杂情况的测试,可以结合Nginx源码进行参考比较好。