如何在FastAPI中使用线程池并行执行GitLab项目查询请求(无需修改现有GitLab调用方法)
如何在FastAPI中使用线程池并行执行GitLab项目查询请求(无需修改现有GitLab调用方法)
我太懂你这种串行请求慢到让人抓狂的感受了——每个请求2秒,几十个ID下来就得等好几分钟,完全没法忍!好在咱们不用修改gitlab.py里的现有方法,利用Python的asyncio就能轻松实现并行请求,效果和你想要的“10线程池”一样高效,甚至更适配异步环境。
问题根源
你之前的代码(包括gitlab.py里的projects函数)都是串行执行的:每调用一次await gitlab.project(id),都得等这个请求完全返回后才会处理下一个ID,自然累积耗时就爆炸了。
解决方案:用asyncio.gather+信号量实现并行
因为gitlab.project(id)本身是异步函数,而且已经用了全局的httpx.AsyncClient(它天生支持并发请求),我们直接用asyncio.gather来同时调度多个异步任务就行。再加个Semaphore控制并发数(比如你想要的10个),避免一下子发太多请求触发GitLab的API限流。
具体代码修改
在你的FastAPI路由里,把原来的串行逻辑改成下面这样:
import asyncio from typing import List, Union # 你的FastAPI路由函数 @app.get("/projects") async def get_projects(ids: List[Union[str, int]]): # 定义信号量,限制同时最多10个并发请求 semaphore = asyncio.Semaphore(10) # 包装一个带信号量的请求函数,确保并发数不超标 async def fetch_project(id: str): async with semaphore: return await gitlab.project(id) # 创建所有异步任务,然后并行执行 tasks = [fetch_project(str(id)) for id in ids] results = await asyncio.gather(*tasks) # 处理返回结果,和你原来的逻辑一致 projects_dict = {} for id, p in zip(ids, results): if p and 'error' not in p: projects_dict[int(id)] = p return projects_dict
为什么这招管用?
asyncio.gather:它会把所有异步任务一次性调度到事件循环中,让它们同时执行(而不是等前一个完成再开始下一个),彻底告别串行等待。Semaphore:相当于一个“并发闸门”,每次只允许10个请求同时发送,既满足你想要的“10线程级并发”,又不会因为请求太密集被GitLab限流。- 无需修改
gitlab.py:我们只是在外层包装了请求逻辑,完全没碰里面的project和projects方法,完美符合你的需求。
额外小贴士
- 可以根据GitLab的API限流规则调整
Semaphore的数值(GitLab默认每分钟允许1000个请求,10个并发是很安全的)。 - 全局的
httpx.AsyncClient已经帮你复用了HTTP连接,这会进一步提升并行请求的效率,不用额外做优化。
备注:内容来源于stack exchange,提问作者MortenB
相关产品推荐
相关产品推荐

