FastAPI启动时如何异步下载大文件避免阻塞事件循环?
解决FastAPI启动时并行下载大文件不阻塞启动的问题
你当前的问题出在@app.on_event("startup")装饰的同步函数会阻塞worker的启动流程——直到下载完成,worker才会进入就绪状态处理请求。大文件下载耗时过长,直接导致worker启动超时,触发大量错误。
下面提供两种可行的解决方案:
方案一:使用后台线程(适合同步下载逻辑)
把下载逻辑放到独立线程中,启动线程后立刻返回,不阻塞startup事件的执行:
from fastapi import FastAPI import threading import requests app = FastAPI() items = {} # 线程安全锁:如果下载线程需要修改共享变量(比如items),必须加锁防止竞态条件 items_lock = threading.Lock() def download_large_file(): try: # 替换为你的实际下载逻辑,这里以requests流式下载为例 url = "https://example.com/your-large-file" with requests.get(url, stream=True) as resp: resp.raise_for_status() with open("large_file.bin", "wb") as f: for chunk in resp.iter_content(chunk_size=8192): f.write(chunk) # 更新共享变量时加锁 with items_lock: items["download_status"] = "completed" except Exception as e: # 记录下载错误,根据需求处理 with items_lock: items["download_status"] = f"failed: {str(e)}" @app.on_event("startup") def startup_event(): # 启动后台守护线程,主进程退出时线程会自动终止 download_thread = threading.Thread(target=download_large_file, daemon=True) download_thread.start()
方案二:使用异步任务(适合异步下载逻辑)
如果你的下载库支持异步(比如aiohttp),可以用asyncio.create_task把下载任务丢到后台异步执行:
from fastapi import FastAPI import asyncio import aiohttp app = FastAPI() items = {} async def download_large_file(): try: url = "https://example.com/your-large-file" async with aiohttp.ClientSession() as session: async with session.get(url) as resp: resp.raise_for_status() with open("large_file.bin", "wb") as f: async for chunk in resp.content.iter_chunked(8192): f.write(chunk) items["download_status"] = "completed" except Exception as e: items["download_status"] = f"failed: {str(e)}" @app.on_event("startup") async def startup_event(): # 创建异步任务,后台执行,不阻塞startup流程 asyncio.create_task(download_large_file())
关键注意事项
- 线程/任务的生命周期:使用守护线程或异步任务时,若FastAPI进程意外退出,未完成的下载会被终止。如果需要确保下载完成,可额外添加进程级的监控逻辑。
- 共享资源安全:如果下载线程/任务需要修改全局变量(比如
items),必须用锁(线程场景)或确保异步操作的原子性(异步场景),避免竞态条件。 - 错误处理:务必给下载逻辑添加异常捕获,避免后台任务崩溃影响主应用。
内容的提问来源于stack exchange,提问作者AP1709
相关产品推荐
相关产品推荐

