(八)FastAPI的异步编程与性能优化

1,328 阅读5分钟

FastAPI 以其快速、现代和易用的特性迅速崭露头角。其核心之一便是对异步编程的强大支持,从而带来卓越的性能表现。在本文中,我们将深入探讨 FastAPI 中的异步编程及其性能优化方法,帮助你充分发挥 FastAPI 的潜力。

一、异步编程基础

1.1 什么是异步编程

异步编程允许程序在执行耗时操作时不阻塞主线程,从而提高并发性能。Python 中的 asyncio 库提供了异步编程支持,通过 asyncawait 关键字实现异步函数。

1.2 异步编程在 FastAPI 中的应用

FastAPI 原生支持异步编程,允许开发者轻松编写高性能的异步 API。

from fastapi import FastAPI
import asyncio

app = FastAPI()

@app.get("/async")
async def async_endpoint():
    await asyncio.sleep(1)
    return {"message": "This is an async endpoint"}

在上述代码中,async_endpoint 是一个异步函数,使用 await asyncio.sleep(1) 模拟了一个耗时操作。FastAPI 会自动处理这些异步函数,确保高效执行。

二、如何编写异步 API

2.1 异步数据库操作

使用异步数据库驱动是实现异步编程的重要一环。例如,使用 databases 库来进行异步数据库操作。

from fastapi import FastAPI
from databases import Database

DATABASE_URL = "sqlite:///./test.db"
database = Database(DATABASE_URL)

app = FastAPI()

@app.on_event("startup")
async def startup():
    await database.connect()

@app.on_event("shutdown")
async def shutdown():
    await database.disconnect()

@app.get("/users")
async def get_users():
    query = "SELECT * FROM users"
    return await database.fetch_all(query)

2.2 异步 HTTP 请求

使用 httpx 库可以进行异步 HTTP 请求。

import httpx

@app.get("/external")
async def call_external_api():
    async with httpx.AsyncClient() as client:
        response = await client.get('https://api.example.com/data')
    return response.json()

2.3 处理并发任务

异步编程还可以用来处理并发任务。例如,使用 asyncio.gather 并发执行多个异步任务。

@app.get("/parallel")
async def parallel_tasks():
    async def task1():
        await asyncio.sleep(1)
        return "Task 1 complete"

    async def task2():
        await asyncio.sleep(2)
        return "Task 2 complete"

    results = await asyncio.gather(task1(), task2())
    return {"results": results}

asyncio.gather 是 Python 异步编程中的一个常用函数,用于并行执行多个异步任务(coroutines),并等待所有任务完成。await 关键字用于等待这些任务的结果,results变量接收所有任务的返回值,结果会以列表形式返回。

三、性能优化技巧

3.1 使用高效的 Web 服务器

FastAPI 需要配合高效的 ASGI 服务器,如 uvicorndaphne

uvicorn main:app --host 0.0.0.0 --port 8000 --reload

3.2 利用缓存

3.2.1 使用 lru_cache 缓存

functools.lru_cache 是 Python 标准库中的一个装饰器,用于缓存函数的结果。它将函数的返回值存储在内存中,以便在下次调用相同参数时,可以直接返回缓存的结果,而不需要重新计算。

from fastapi import FastAPI
from functools import lru_cache

app = FastAPI()

@lru_cache(maxsize=32)
def get_data():
    # 模拟一个耗时操作,如数据库查询
    return {"data": "some expensive data"}

@app.get("/data")
def read_data():
    return get_data()
lru_cache 参数
  • maxsize: 指定缓存的最大容量。当缓存达到此大小时,最近最少使用(Least Recently Used,LRU)的条目将被移除。maxsize 的默认值是 128。如果将 maxsize 设为 None,缓存将无限制增长。
使用场景和注意事项
  • 使用场景: 适用于那些需要频繁调用且计算开销大的函数,但函数参数较少、结果值也较少的情况。
  • 注意事项: 由于缓存的数据存储在内存中,因此对于大型数据集或大量不同参数的函数调用,内存消耗可能会迅速增加,必须合理设置 maxsize。

lru_cache 非常适合在 FastAPI 应用中使用,可以显著提高某些计算密集型操作的性能。结合 FastAPI 的接口,可以简单有效地缓存计算结果,避免重复计算。

3.2.2 使用 Redis 缓存

Redis 是一个流行的分布式缓存解决方案,适用于更复杂的缓存需求。

from fastapi import FastAPI
import redis
import json

app = FastAPI()
redis_client = redis.StrictRedis(host='localhost', port=6379, db=0)

def get_expensive_data():
    # 模拟一个耗时操作,如数据库查询
    return {"data": "some expensive data"}

@app.get("/data")
def read_data():
    cached_data = redis_client.get("data")
    if cached_data:
        return json.loads(cached_data)
    
    data = get_expensive_data()
    redis_client.setex("data", 300, json.dumps(data))  # 缓存时间300秒
    return data

在 FastAPI 中使用 Redis 进行缓存,通过安装 Redis 客户端库,连接 Redis,并在处理请求时将结果存储到 Redis,以减少重复计算,提高响应速度。

8-redis缓存.png

3.3 优化数据库查询

尽量减少数据库查询次数,并使用异步数据库驱动。利用 ORM(如 Tortoise ORM 或 SQLAlchemy)进行优化查询。

3.4 配置连接池

配置数据库连接池,提高数据库访问性能。以下是使用 databases 库的示例:

database = Database(DATABASE_URL, min_size=5, max_size=20)

3.5 使用 Cython 或者 PyPy

对于 CPU 密集型任务,可以考虑使用 Cython 将部分代码编译为 C 扩展,或使用 PyPy 代替 CPython 解释器。

四、总结

通过本文的详细介绍,我们探讨了 FastAPI 中的异步编程和性能优化方法。从异步编程基础,到如何编写异步 API,再到具体的性能优化技巧,希望你能掌握这些方法并应用于实际项目中。利用 FastAPI 强大的异步支持和丰富的优化手段,你可以构建出高性能的 Web 应用。

异步编程和性能优化是现代 Web 开发的重要内容,掌握这些技巧能够让你在开发过程中事半功倍。希望本文能为你的 FastAPI 开发之旅提供有益的帮助。