type
status
date
slug
summary
tags
category
icon
password
FastAPI 以其出色的性能和开发体验而闻名,但要真正发挥其潜力,你需要掌握一些关键的优化技巧。本文将分享 10 个实用的性能优化建议,涵盖从异步编程最佳实践到测试框架选择的方方面面。无论你是 FastAPI 新手还是经验丰富的开发者,这些技巧都将帮助你构建更快、更可靠的应用程序。
1. 安装
uvloop 和 httptools如果在你的环境中安装了 Uvicorn,它会自动使用它们。
uvloop 无法在 Windows 上安装。如果你在本地使用 Windows,但在生产环境中使用 Linux,你可以使用环境标记来避免在 Windows 上安装 uvloop,例如 uvloop; sys_platform != 'win32' 。2. 谨慎使用非异步函数
在 FastAPI 中使用非异步函数会产生性能损耗。因此,应始终优先使用异步函数。性能损耗来自于 FastAPI 将调用
run_in_threadpool,它会使用线程池运行该函数。在内部,
run_in_threadpool 会使用 anyio.to_thread.run_sync 在线程池中运行该函数。线程池中只有 40 个线程可用。如果使用完所有线程,你的应用程序将被阻塞。
要改变可用的线程数,可以使用以下代码:
你可以在 AnyIO 的文档中阅读更多信息。
3. 在 WebSocket 上使用
async for 而不是 while True你在互联网上找到的大多数示例都使用
while True 来读取 WebSocket 中的消息。我认为使用这种更不优雅的写法主要是因为 Starlette 文档长期没有展示
async for 的写法。与使用
while True 相比:你可以使用
async for 记号:你可以在 Starlette 文档中阅读更多相关内容。
4. 忽略
WebSocketDisconnect 异常如果你使用
while True 记号,你需要捕获 WebSocketDisconnect。async for 记号会自动为你捕获它。如果你需要在 WebSocket 断开连接时释放资源,你可以使用该异常来实现。
如果您使用的是较旧的 FastAPI 版本,只有
receive 方法会抛出 WebSocketDisconnect 异常。send 方法不会抛出它。在最新版本中,所有方法都会抛出它。在这种情况下,您需要将 send 方法添加到 try 块中。5. 使用 HTTPX 的
AsyncClient 而不是 TestClient由于您在应用程序中使用
async 函数,使用 HTTPX 的 AsyncClient 会更容易 而不是 Starlette 的 TestClient。考虑通过 GitHub Sponsors 支持
asgi-lifespanFlorimond Manca 的创建者。6. 使用 Lifespan State 而不是
app.state不久前,FastAPI 开始支持 lifespan state,它定义了一种标准方式来管理需要在启动时创建并在请求-响应周期中使用的对象。
不再推荐使用
app.state。你应该改用 lifespan state。使用
app.state,你可以这样做:使用生命周期状态,你可以这样做:
7. 启用 AsyncIO 调试模式
如果你想找到阻塞事件循环的端点,可以启用 AsyncIO 调试模式。
启用后,当任务执行耗时超过 100ms 时,Python 会打印警告消息。
使用
PYTHONASYNCIODEBUG=1 python main.py 运行以下代码:如果你调用该端点,你会看到以下消息:
你可以在 官方文档 中了解更多信息。
8. 实现纯 ASGI 中间件而不是
BaseHTTPMiddlewareBaseHTTPMiddleware 是在 FastAPI 中创建中间件的最简单方式。注意
@app.middleware("http") 装饰器是 BaseHTTPMiddleware 的包装器。BaseHTTPMiddleware 曾存在一些问题,但在最新版本中大多数问题都已修复。不过,使用它仍然会带来性能损耗。为了避免性能损耗,你可以实现一个纯 ASGI 中间件 。缺点是实现起来更加复杂。
检查 Starlette 的文档来了解如何实现纯 ASGI 中间件 。
9. 你的依赖项可能在线程上运行
如果函数是非异步的,并且你将其用作依赖项,它将在线程中运行。
在下面的示例中,
http_client 函数将在一个线程中运行:要在事件循环中运行,你需要将函数设为异步:
你可以使用
python main.py 运行以下代码:如果你调用该端点,你会看到以下消息:
将
def http_client 替换为 async def http_client 并重新运行应用程序。你将看不到 Threads in use: 1 这条消息,因为该函数在事件循环中运行。10. 使用
pytest.mark.anyio 而不是 pytest.mark.asyncio默认情况下,
anyio 会将每个标记的测试运行两次,一次使用 trio,另一次使用 asyncio。如果你正在测试一个应用程序而不是一个包,你可能想通过使用其中一个来限制这个行为:- 作者:SupraYou
- 链接:http://blog.suprayou.com/%E4%BA%A7%E5%93%81%E7%9C%9F%E7%BB%8F/fastapi-performance-optimization-10-practical-tips
- 声明:本文采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处。
