02. 为什么很多大模型系统需要异步任务和消息队列?

整理异步任务、消息队列与大模型系统削峰解耦方案。

简单回答

因为大模型推理耗时长(秒级到分钟级)、资源消耗大(GPU),如果用同步调用,HTTP 连接会长时间被占用,前端卡死,后端线程池耗尽。异步任务和消息队列可以解耦请求的提交和处理,实现流量削峰、重试、优先级调度,保证系统在高并发下不崩。

详细解释

同步调用的问题

传统 API 的调用链路通常是:客户端发请求 → 服务端处理 → 返回结果,整个过程几十毫秒完成。但 LLM 推理一个请求可能要几秒到几十秒(长文本甚至几分钟),如果用同步方式,HTTP 连接在这段时间一直被占用,服务端的工作线程也被阻塞,并发能力极低。假设一个服务有 200 个工作线程,每个请求耗时 10 秒,吞吐就只有 20 QPS。

更糟糕的是,LLM 推理需要 GPU,而 GPU 资源是有限的。当请求量超过 GPU 处理能力时,如果没有队列缓冲,后续请求要么超时要么被直接拒绝,用户体验很差。

消息队列的作用

引入消息队列(如 Redis Queue、RabbitMQ、Kafka、Celery)后,请求流程变成:客户端提交请求 → 业务层把任务放入队列 → 立即返回任务 ID → 推理 worker 从队列中消费任务 → 处理完后把结果写入存储 → 客户端轮询或通过 WebSocket 接收结果。

这样做的好处有几个。第一是流量削峰,请求高峰时队列可以缓冲任务,推理 worker 按自己的节奏消费,不会被瞬时流量打垮。第二是解耦,业务层和推理层通过队列通信,两边可以独立扩缩容。推理层可以按 GPU 利用率来动态调整 worker 数量。第三是重试和容错,任务在队列里如果处理失败可以自动重试,不需要客户端重新发请求。第四是优先级调度,可以为不同用户或不同类型的任务设置不同优先级,VIP 用户的请求优先处理。

哪些场景特别需要异步

批量处理场景是最典型的。比如对一批文档做摘要、翻译、分类,几百个任务丢进队列慢慢处理,用户来查结果就行。Agent 场景中,一个任务可能需要多次调用 LLM + 多次工具调用,整个链路耗时很长,同步等待不现实。离线评测和数据标注,批量跑模型打分、生成训练数据等。异步文件处理,比如上传一个 PDF,后端要做 OCR + 切分 + 向量化,整个流程可能需要几分钟。

流式输出和异步的关系

很多人会问:如果用了流式输出(SSE),还需要异步队列吗?答案是看场景。对于交互式对话场景,流式 SSE 本身就是一种"半异步"方案——客户端不需要等全部完成就能看到输出。但即使用了流式,推理层仍然需要队列来做排队和调度,否则并发量一大就会 GPU 过载。对于批量任务和后台任务,异步队列是必须的。

工程实践中的常见方案

轻量场景用 Redis + Celery 或 Redis + Bull(Node.js)就够了。重量场景用 Kafka 或 RabbitMQ,Kafka 适合高吞吐日志类任务,RabbitMQ 适合需要复杂路由和优先级的场景。有些团队会直接用云服务的任务队列(如 AWS SQS + Lambda)。推理层如果用 vLLM,它内部有 continuous batching 机制,本身就带了一层请求队列。

面试时可以这样答

大模型推理的核心特点是耗时长、资源重。一个请求几秒到几十秒,同步调用会占用大量连接和线程,GPU 也很快被打满。异步任务和消息队列就是为了解决这个问题。

具体作用有三个。第一是削峰,队列缓冲高峰请求,推理 worker 按自己的处理能力消费。第二是解耦,业务层和推理层独立扩缩容,推理层可以按 GPU 利用率调整。第三是重试和优先级,任务失败自动重试,VIP 用户可以插队。

最典型的场景是批量处理——几百个文档的摘要任务丢进队列慢慢跑。Agent 场景也需要,因为一个任务可能调用多次 LLM,同步等太久了。即使是交互式对话用了流式输出,推理层也还是需要队列做排队调度。

技术选型上,轻量场景 Redis + Celery 就够了,重量场景上 RabbitMQ 或 Kafka。如果用 vLLM 这类推理框架,它内部已经有 continuous batching,算是自带了一层队列。

常见追问

  1. 异步任务的结果怎么通知客户端?轮询还是推送?各有什么优缺点?
  2. 队列里的任务积压太多怎么处理?有哪些降级策略?
  3. 如果推理 worker 挂了,正在处理的任务怎么保证不丢?