You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

FastAPI如何在顺序处理请求时保持服务响应性?

问题描述

我需要给服务器提供一个处理GPU密集型渲染任务的API,该任务同一时间仅能处理一个请求。客户端提交任务后应立即收到201 ACCEPTED响应,任务处理时长可达1分钟,可能存在数十个待处理请求。

我编写了最小可复现示例代码:

import time
import asyncio
from fastapi import FastAPI, status


app = FastAPI()
fifo_queue = asyncio.Queue()


async def process_requests():
    while True:
        name = await fifo_queue.get()  # 等待队列中的请求
        print(name)
        time.sleep(10)  # 阻塞线程的资源密集型任务
        fifo_queue.task_done()  # 标记请求处理完成


@app.on_event("startup")
async def startup_event():
    asyncio.create_task(process_requests())  # 启动请求处理任务


@app.get("/render")
async def render(name):
    fifo_queue.put_nowait(name)  # 将请求参数加入队列
    return status.HTTP_201_CREATED  # 返回201状态码

当前方案存在问题:服务器无法保持响应性,处理第一个请求后便持续忙碌,无法正常响应后续请求。

示例中仅需将time.sleep(10)替换为await asyncio.sleep(10)即可解决问题,但实际场景中此方法不适用。请问有什么可行的解决思路?

可行解决思路

1. 用线程池执行阻塞任务

把GPU密集型的阻塞任务放到线程池中运行,避免卡住asyncio的事件循环。Python 3.9+自带asyncio.to_thread()直接使用,也可以手动用concurrent.futures.ThreadPoolExecutor管理:

修改后的process_requests函数:

async def process_requests():
    while True:
        name = await fifo_queue.get()
        print(name)
        # 把阻塞任务丢去线程池执行
        await asyncio.to_thread(run_gpu_render, name)
        fifo_queue.task_done()

def run_gpu_render(name):
    # 这里写实际的GPU渲染代码
    time.sleep(10)  # 模拟阻塞任务

2. 用进程池处理计算密集型任务

如果GPU任务是纯计算密集型,线程池受GIL限制效率不高,就用进程池。注意任务参数要能序列化,还要确保子进程能正确访问GPU资源:

from concurrent.futures import ProcessPoolExecutor

# 限制只开一个进程,保证同一时间处理一个任务
executor = ProcessPoolExecutor(max_workers=1)

async def process_requests():
    while True:
        name = await fifo_queue.get()
        print(name)
        loop = asyncio.get_running_loop()
        # 提交任务到进程池
        await loop.run_in_executor(executor, run_gpu_render, name)
        fifo_queue.task_done()

def run_gpu_render(name):
    time.sleep(10)  # 模拟GPU渲染任务

3. 拆分任务到独立服务

把渲染任务完全剥离成独立进程或服务,比如用Celery配合Redis/RabbitMQ做任务队列:

  • FastAPI只负责接请求,把任务参数丢进消息队列,直接返回201
  • 单独跑一个worker进程(只开一个实例)监听队列,执行GPU渲染
  • 还可以加个查询任务状态的API,让客户端查进度

这种方式解耦彻底,适合长期跑大量任务的场景,后续要加GPU并行也方便扩展worker数量。

4. 用asyncio子进程执行外部渲染命令

如果渲染任务已经封装成命令行工具,就用asyncio.create_subprocess_exec()启动子进程,天然不阻塞事件循环:

async def process_requests():
    while True:
        name = await fifo_queue.get()
        print(name)
        # 启动子进程运行渲染命令
        proc = await asyncio.create_subprocess_exec(
            "your-render-cli", name,
            stdout=asyncio.subprocess.PIPE,
            stderr=asyncio.subprocess.PIPE
        )
        await proc.wait()  # 等子进程跑完
        fifo_queue.task_done()

内容的提问来源于stack exchange,提问作者Lukasz Tracewski

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.01 21:07:01