云计算百科
云计算领域专业知识百科平台

AI应用后端工程(05):用户同时发来很多请求,AI 后端怎么避免被压垮?

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */
#content_views .toc,
/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */
#content_views.markdown_views > p:empty:has(+ .toc),
#content_views.markdown_views > .toc + p:empty,
/* 富文本旧版目录标记 */
#content_views.htmledit_views #main-toc,
#content_views.htmledit_views #hr-toc,
#content_views.htmledit_views p[id*=\”-toc\”] {
display: none !important;
}
/* 目录去掉后,紧跟的首个标题不再多出一块上边距 */
#content_views.markdown_views > .toc + h1,
#content_views.markdown_views > .toc + h2,
#content_views.markdown_views > .toc + h3,
#content_views.markdown_views > .toc + h4,
#content_views.markdown_views > .toc + p:empty + h1,
#content_views.markdown_views > .toc + p:empty + h2,
#content_views.markdown_views > .toc + p:empty + h3,
#content_views.markdown_views > .toc + p:empty + h4 {
margin-top: 0 !important;
}

目录

先分清:每秒请求数和同时处理数

异步可以减少等待浪费,但不会让容量变成无限

用并发上限保护模型调用

队列可以削峰,但不能把过载变没

给一次请求设置总时间预算

上游额度和后端容量是两道不同的闸门

第一层:自己的应用容量

第二层:模型服务的容量

满载时,快速拒绝有时比继续等待更好

不能只看平均响应时间

最后

Hello,大家好,我是 1M55!👋

平时只有几个人使用 AI 应用时,请求看起来很简单:用户提问,后端调用模型,模型返回回答。

但如果很多用户同时提问,情况就变了。有人一直看到“正在生成”,有人等了很久才收到第一个字,还有人直接遇到超时或错误。模型服务可能返回 429,后端的连接数和内存也可能持续上涨。

这时,简单地让后端“多接一点请求”通常解决不了问题。请求进来以后要占用资源,模型生成时间又不固定。如果请求进入系统的速度长期快于系统处理速度,等待中的请求就会越积越多。

所以,AI 后端需要回答的不是“能不能接收请求”,而是:

系统的处理能力有限时,怎么控制并发、管理等待,并在过载时尽早给用户明确反馈?

0.先分清:每秒请求数和同时处理数

讨论容量时,常见两个概念:请求速率和并发数。

  • 请求速率:单位时间内有多少请求进入系统,常用 QPS(每秒查询数)描述。
  • 并发数:某个时刻有多少请求还没有处理完成。

它们有关联,但不是一回事。

假设每秒有 5 个请求进入,每个请求平均需要 10 秒才结束,那么系统里平均可能有约 50 个请求正在处理中。可以用一个简单关系理解:

平均在途请求数 ≈ 每秒到达请求数 × 平均处理时间

这就是 Little’s Law 在稳定系统中的基本形式。它不是用来精确计算某台服务器能扛多少请求的公式,但能解释一个重要现象:请求处理时间越长,即使进入速率不变,同时占用的请求资源也越多。

AI 请求尤其容易出现这种情况。一次生成可能需要等待模型首段输出,再持续接收许多内容片段。用户看到的只是一个对话窗口,后端可能同时维护着下游浏览器连接、上游模型连接和生成任务。

DeepSeek 文档也把一次模型请求从发出到模型响应完成都计入占用中的并发连接;超过账户并发限制时,请求会收到 429。具体额度会随模型和账户而不同,应该以服务商当前文档和账户配置为准。DeepSeek 限流与并发说明

1.异步可以减少等待浪费,但不会让容量变成无限

后端使用异步 HTTP 客户端调用模型时,等待网络响应期间,程序通常可以去处理其他协程。这比让一个线程一直阻塞在网络等待上更有效率。

但“异步”不代表请求不占资源。

一个正在生成的请求仍然可能占用:

  • 一个用户侧的连接;
  • 一个到模型服务的上游请求;
  • 应用中的任务和上下文;
  • HTTP 连接池中的连接或连接额度;
  • 缓冲区和队列空间;
  • 模型服务提供的并发额度。

因此,异步改善的是等待期间如何调度执行,不是消除了连接、内存和上游并发的上限。如果后端不设限制,过多请求仍然可以同时堆在系统里。

流式接口更需要留意这一点:请求不会在模型开始输出时就结束,而是要持续到模型生成完成、用户主动停止或连接异常结束。也就是说,单次请求处理时间长,活动请求就可能长期占着系统容量。

2.用并发上限保护模型调用

最直接的保护方式之一,是限制同时发给模型的请求数。

例如,应用设置一个并发上限为 C:最多允许 C 个请求进入模型调用阶段。容量已满时,新请求不能继续无条件进入上游,而应进入一个有界等待区,或者被快速拒绝。

在单个 Python 进程里,可以用 asyncio.Semaphore 限制协程同时进入某段代码的数量。信号量计数归零时,后续任务会等待;离开代码块后再释放额度。Python asyncio 同步原语文档

概念上像这样:

model_slots = asyncio.Semaphore(20)

async def call_model(request):
async with model_slots:
return await send_request_to_model(request)

这段代码只能说明并发闸门的基本思路。生产系统还要考虑等待超时、异常释放、客户端断开时的清理,以及如何将额度和监控指标接入应用。

更关键的是:这个信号量只在当前进程内有效。如果部署了 4 个进程,每个进程各自允许 20 个并发,那么整个服务理论上可能同时发出约 80 个模型请求。若再有多台服务器,这个总量还会增加。

所以要明确你限制的范围:

  • 单进程限制:保护当前进程内的资源;
  • 单实例或单机限制:协调同一台机器上的多个工作进程;
  • 全服务限制:跨机器共同遵守一个总额度;
  • 账户或模型供应商限制:避免所有业务流量合计超过上游额度。

后两种通常需要共享协调机制,或者由集中式队列、网关、限流服务来管理。把每个进程里的信号量都设置成相同数字,并不等于整个集群有同样的全局上限。

3.队列可以削峰,但不能把过载变没

并发槽位满了以后,可以让请求排队。这能吸收短时间的流量尖峰:比如一小批请求同时到达,但前面的任务很快就会完成,后面的请求等一会儿仍能被处理。

问题在于,队列只能等待已有容量释放,不能增加模型每秒能处理的工作量。

如果流量持续高于处理能力,队列长度就会持续增加。用户的等待时间越来越长,请求可能还没轮到执行就已经超时。最终,大量请求同时失败,后端还要承担排队任务、连接和重试带来的额外压力。

因此,队列必须有边界,通常至少考虑:

  • 最大排队数量:最多允许多少请求等待;
  • 最大排队时间:一个请求最多等多久;
  • 队列满时的动作:拒绝、降级,或提示用户稍后重试;
  • 用户间的公平性:避免一个用户或租户占满所有等待位置。
  • Python 的 asyncio.Queue(maxsize=…) 可以设置队列上限;队列满时,`put()` 会等待空位,`put_nowait()` 则会直接抛出队列已满异常。Python asyncio 队列文档

    不过,排队只是系统内部的一种调度策略。面对 HTTP 请求,如果在应用层等待队列空位,用户的连接也可能一直挂着。因此系统要控制等待时间,不能把“放进队列”当成请求已经得到处理。

    4.给一次请求设置总时间预算

    一个请求从进入后端到最终结束,可以拆成几个阶段:

    进入后端 → 等待并发额度 → 等待模型首段响应 → 持续接收生成内容 → 完成或中断

    如果只给模型 HTTP 调用设置超时,却不限制排队时间,用户可能在队列里等很久,等轮到调用模型时又从头开始等待。

    更合理的思路是给整次业务请求一个总时间预算,并在内部划分阶段:

    • 入口处理时间;
    • 等待并发额度的时间;
    • 等待模型首段响应的时间;
    • 模型持续生成的时间;
    • 完成响应和清理资源的时间。

    比如用户最多愿意等 30 秒,那么后端不能先让他在队列里等 29 秒,再开始一次可能持续一分钟的生成。排队阶段必须消耗同一个总预算。剩余时间不足以完成请求时,应尽早拒绝或提示用户,而不是继续占用资源。

    对于流式请求,还要区分“总生成时限”和“单次读取超时”。模型可能很久不发首段内容,也可能稳定地持续输出;这两类情况需要不同的监控和处理方式。超时设置应结合业务目标、模型特性和实测延迟确定,不能简单照搬一个固定数字。

    5.上游额度和后端容量是两道不同的闸门

    即使自己的服务器还有空余资源,模型服务也可能拒绝请求。例如,上游账户的并发数、QPS 或其他配额已经达到上限。

    反过来,模型账户还有额度,也不代表自己的后端应该无限并发地发送请求。应用还需要保留容量给鉴权、会话管理、数据库访问、连接转发和其他业务功能。

    因此通常要同时管理两层容量:

    第一层:自己的应用容量

    后端决定自己能同时承载多少生成请求、允许多少请求排队、队列满时怎样处理。

    第二层:模型服务的容量

    后端还要遵守模型供应商的并发、速率和账户限制,并处理 429、超时和服务过载等响应。DeepSeek 将 429 用于表示请求发送过快,或超过并发、QPS 等限制。DeepSeek 错误码文档

    这两层限制需要相互协调。只按模型服务额度放量,可能先把自己的连接池打满;只按自己的容量放量,也可能频繁撞上供应商限制。后端应在上游允许额度和自身可承受容量之间,设置实际调用上限。

    6.满载时,快速拒绝有时比继续等待更好

    后端拒绝请求听起来像服务能力不足,但在持续过载时,有边界地拒绝往往比接收所有请求更可控。

    如果系统已经没有并发位置,队列也达到上限,继续接请求可能只会造成更长等待和更多超时。应用可以返回明确的“当前繁忙,请稍后重试”,并使用适当的状态码表达限流或服务暂不可用。具体选择要与接口语义一致,前端也要能正确展示。

    这比让页面无限转圈更好,因为用户至少知道请求没有进入处理阶段。后端也能保护已有请求,避免过载扩散。

    重试策略也需要谨慎。收到限流或暂时性服务错误时,客户端可以按退避策略稍后重试,但不能让所有用户在同一时刻立即重发。否则流量恢复时会再次形成尖峰。对于已经开始输出的流式请求,盲目自动重试还可能造成重复生成和额外成本。

    7.不能只看平均响应时间

    服务看起来变慢时,平均响应时间往往不够用。少数请求可能排队很久、首字很慢,平均值却没有明显变化。

    可以分别观察:

    • 当前活跃生成请求数;
    • 等待队列长度;
    • 排队时间的分布;
    • 模型首段响应延迟;
    • 完整生成耗时;
    • 上游 429、5xx 和超时比例;
    • 用户断开连接的数量;
    • 取消后资源释放所需时间。

    这些指标能帮助区分问题发生在哪一段:

    • 排队时间升高:入口流量超过当前并发处理能力;
    • 首段延迟升高:模型服务响应变慢,或上游正在排队;
    • 生成耗时升高:输出更长、模型生成速度变化,或网络读取受阻;
    • 429 增加:可能触达上游配额或速率限制;
    • 断连增加但活跃请求不降:需要检查取消和资源清理链路。

    容量治理的目标不是让所有请求永远成功,而是在有限资源下,让系统行为可预期:能处理的请求正常完成;短时尖峰可以有界等待;超过边界的请求尽早得到反馈。

    最后

    AI 请求比普通接口更容易长期占用连接和任务资源。异步能提高等待期间的调度效率,但不会消除并发上限;队列能吸收短时尖峰,但无法提高持续处理能力。

    要避免后端被请求压垮,需要把容量控制做成一条完整链路:

    • 限制并发,保护应用和模型调用;
    • 设置有界队列,避免等待无限累积;
    • 给排队和生成设定时间预算;
    • 分别管理自身容量与上游配额;
    • 满载时快速反馈,并监控请求卡在哪个阶段。

    系统容量有限时,可靠的后端不是把每个请求都接进来,而是让每个请求都有清楚的去向:正在处理、有限等待,或者明确拒绝。

    我是 1M55,我们下篇见!

    赞(0)
    未经允许不得转载:网硕互联帮助中心 » AI应用后端工程(05):用户同时发来很多请求,AI 后端怎么避免被压垮?
    分享到: 更多 (0)

    评论 抢沙发

    评论前必须登录!