我正在使用 FastAPI+Sqlalchemy+Mysql 构建应用程序,当我进行压力测试时,出现 Asgi 异常错误:
[ERROR] Exception in ASGI application,
RecursionError: maximum recursion depth exceeded while calling a Python object.
我绝对可以拥有:
- 我没有调用任何递归函数
- 我已经尝试过 alchemy 和 mysql_connector 来查询数据库,但错误仍然存在。
代码在这里:
我的 main.py 如下
def create_app()->FastAPI:
app = FastAPI(debug = False)
register_cors(app)
app.include_router(admin_router)
register_exception(app)
register_token_validate(app)
return app
***def register_cors(app:FastAPI):
@app.middleware("http")
async def cors(request:Request, call_next):
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
response = await call_next(request)
response.headers["access-control-allow-credentials"] = "true"
response.headers["access-control-allow-origin"] = "*"
response.headers["access-control-allow-methods"] = "*"
return response***
def register_exception(app:FastAPI):
@app.exception_handler(RequestValidationError)
async def validation_exception_handler(request: Request, exc: RequestValidationError):
errType = (exc.errors())[0]["type"]
detail = (exc.errors())[0]
type = errType.split(".")
if type[0] == "value_error":
return JSONResponse(
status_code=422,
content={"code": 422,"msg": "value error", "errinfo_type":detail}
)
elif type[0] == "type_error":
return JSONResponse(
status_code=422,
content={"code": 422,"msg": "type error", "errinfo_type":detail}
)
else:
return JSONResponse(
status_code=422,
content={"code": 422,"msg": "server error", "errinfo_type":detail}
)
@app.exception_handler(Exception)
async def all_exception_handler(request: Request, exc: Exception):
return JSONResponse(
status_code=500,
content={"code": 500,"msg": "server error", "errinfo_type":None}
)
def register_token_validate(app: FastAPI):
@app.middleware("http")
async def token_validate(request: Request, call_next):
excludeUrlL = [
"/v1/login",
"/docs",
"/redoc"
]
if (request.url.path not in excludeUrlL) and (request.method != "OPTIONS"):
res = security.validate_token(request.headers.get("token"), request.headers.get("request_time"))
if res['status'] == "success":
response = await call_next(request)
response.headers["access-control-allow-credentials"] = "true"
response.headers["access-control-allow-origin"] = "*"
response.headers["access-control-allow-methods"] = "*"
return response
elif res['status'] == "failed":
return JSONResponse(
status_code=401,
content={"code": 401,"msg": "token expired"}
)
elif res['status'] == "updated":
response = await call_next(request)
response.headers["token"] = res['payload']
response.headers["access-control-allow-credentials"] = "true"
response.headers["access-control-allow-origin"] = "*"
response.headers["access-control-expose-headers"] = "token"
response.headers["access-control-allow-methods"] = "*"
return response
else:
response = await call_next(request)
response.headers["access-control-allow-credentials"] = "true"
response.headers["access-control-allow-origin"] = "*"
response.headers["access-control-allow-methods"] = "*"
return response
app = create_app()
if __name__ == "__main__":
import uvicorn
uvicorn.run(app="main:app", host="0.0.0.0", port=8000, reload=True, debug=False)
安全.py
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from datetime import datetime, timedelta, tzinfo
from db.DbSession import session
from sqlalchemy import text
from config import config
from core.logger import logger
from core import util
import pytz
def validate_token(token, request_time):
if not token:
return {"status":"failed", "payload":None, "msg": "token is required"}
if not request_time:
return {"status":"failed", "payload":None, "msg": "request_time is required"}
sql = "select id, mobile, token, expired_time, last_request_time from my_user_table where token = :token limit 1"
rp = session.execute(text(sql), {"token":token})
res = [dict(r.items()) for r in rp]
if not res:
return {"status":"failed", "payload":None, "msg": "token error"}
nowStamp = datetime.now().timestamp()
expired_timeStamp = res[0]['expired_time'].timestamp()
if nowStamp > expired_timeStamp:
return {"status":"failed", "payload":None, "msg": "token is expired"}
if (int(request_time) - int(res[0]['last_request_time'])) > 600:
return {"status":"updated", "payload":token, "msg": "token is updated"}
else:
return {"status":"success", "payload":None, "msg": "token is valid"}
会话.py
from sqlalchemy import create_engine
from sqlalchemy.orm import Session
from sqlalchemy.pool import NullPool
engine = create_engine(config.DbUrl, echo = False, poolclass=NullPool)
session = Session(engine)
api.py
@router.get("/v1/getCompany", summary="get company information")
async def getCompany(id:int):
sql = "select * from my_company_table c where c.id = :id"
rp = session.execute(text(sql), {"id":id})
res = [dict(r.items()) for r in rp]
session.commit()
return util.resp_200(200, 'get company information success', res)
/v1/getCompany
当我连续调用这个接口时,我得到了上面的错误
[ERROR] Exception in ASGI application
Traceback (most recent call last):
File "/root/.cache/pypoetry/virtualenvs/facli-zsB1kvQc-py3.8/lib/python3.8/site-packages/uvicorn/protocols/http/h11_impl.py", line 388, in run_asgi
result = await app(self.scope, self.receive, self.send)
File "/root/.cache/pypoetry/virtualenvs/facli-zsB1kvQc-py3.8/lib/python3.8/site-packages/uvicorn/middleware/proxy_headers.py", line 45, in __call__
return await self.app(scope, receive, send)
File "/root/.cache/pypoetry/virtualenvs/facli-zsB1kvQc-py3.8/lib/python3.8/site-packages/fastapi/applications.py", line 179, in __call__
await super().__call__(scope, receive, send)
File "/root/.cache/pypoetry/virtualenvs/facli-zsB1kvQc-py3.8/lib/python3.8/site-packages/starlette/applications.py", line 111, in __call__
await self.middleware_stack(scope, receive, send)
File "/root/.cache/pypoetry/virtualenvs/facli-zsB1kvQc-py3.8/lib/python3.8/site-packages/starlette/middleware/errors.py", line 181, in __call__
raise exc from None
File "/root/.cache/pypoetry/virtualenvs/facli-zsB1kvQc-py3.8/lib/python3.8/site-packages/starlette/middleware/errors.py", line 159, in __call__
await self.app(scope, receive, _send)
File "/root/.cache/pypoetry/virtualenvs/facli-zsB1kvQc-py3.8/lib/python3.8/site-packages/starlette/middleware/cors.py", line 86, in __call__
await self.simple_response(scope, receive, send, request_headers=headers)
File "/root/.cache/pypoetry/virtualenvs/facli-zsB1kvQc-py3.8/lib/python3.8/site-packages/starlette/middleware/cors.py", line 142, in simple_response
await self.app(scope, receive, send)
File "/root/.cache/pypoetry/virtualenvs/facli-zsB1kvQc-py3.8/lib/python3.8/site-packages/starlette/middleware/cors.py", line 86, in __call__
await self.simple_response(scope, receive, send, request_headers=headers)
File "/root/.cache/pypoetry/virtualenvs/facli-zsB1kvQc-py3.8/lib/python3.8/site-packages/starlette/middleware/cors.py", line 142, in simple_response
await self.app(scope, receive, send)
File "/root/.cache/pypoetry/virtualenvs/facli-zsB1kvQc-py3.8/lib/python3.8/site-packages/starlette/middleware/cors.py", line 86, in __call__
await self.simple_response(scope, receive, send, request_headers=headers)
..........
File "/root/.cache/pypoetry/virtualenvs/facli-zsB1kvQc-py3.8/lib/python3.8/site-packages/starlette/middleware/cors.py", line 86, in __call__
await self.simple_response(scope, receive, send, request_headers=headers)
File "/root/.cache/pypoetry/virtualenvs/facli-zsB1kvQc-py3.8/lib/python3.8/site-packages/starlette/middleware/cors.py", line 142, in simple_response
await self.app(scope, receive, send)
File "/root/.cache/pypoetry/virtualenvs/facli-zsB1kvQc-py3.8/lib/python3.8/site-packages/starlette/middleware/cors.py", line 74, in __call__
headers = Headers(scope=scope)
File "/usr/local/lib/python3.8/typing.py", line 873, in __new__
obj = super().__new__(cls)
RecursionError: maximum recursion depth exceeded while calling a Python object
相关的问题我都查过了,可惜没有找到答案,现在只能怀疑是FastAPI或者Starlette的问题,谁能给我解决问题,非常感谢。
经过测试,我可以这样解决这个问题:sys.setrecursionlimit(9000000)
,但我认为这不应该是这种问题的最佳解决方案