vLLM高并发推理时客户端超时与服务端scheduler delay的联调设定

文章导读
vLLM 服务在高并发下出现客户端超时,同时服务端指标显示 scheduler delay 变大,这两个现象通常是同一个排队问题在两端的不同表现。客户端超时是请求从发起到最后拿到数据的整体预算,而 scheduler delay 是请求在 vLLM 等待队列里被调度器选中之前的等待时间。联调的目标不是单纯改大某个值,而是先找出等待时间主要落在哪一段,再分别设定客户端预算与服务端调度参数。
📋 目录
  1. A 先拆链路,定位延迟花在哪个环节
  2. B 服务端 scheduler delay 的常见成因与相应调整
  3. C 客户端超时与重试预算怎么给
  4. D 联调排查顺序
A A

vLLM 服务在高并发下出现客户端超时,同时服务端指标显示 scheduler delay 变大,这两个现象通常是同一个排队问题在两端的不同表现。客户端超时是请求从发起到最后拿到数据的整体预算,而 scheduler delay 是请求在 vLLM 等待队列里被调度器选中之前的等待时间。联调的目标不是单纯改大某个值,而是先找出等待时间主要落在哪一段,再分别设定客户端预算与服务端调度参数。

客户端超时与服务端调度延迟是同一个排队系统的头和尾。调整前先确定目标并发下允许请求等待的总时长,再把这个预算拆到客户端超时与服务端 batch 设置上。不建议孤立调大 timeout,否则只会掩盖调度饥饿;也不建议为了降延迟把并发压到过低,否则吞吐会先掉。

先拆链路,定位延迟花在哪个环节

vLLM 服务的完整链路包括:连接建立、排队等待调度(scheduler delay 的主要来源)、预填充、解码、响应传输。客户端超时设置是总预算,服务端 scheduler delay 是其中的一项。启动 vLLM 服务时,建议同时打开 Prometheus metrics,请求超时后才能区分超时发生在连接阶段还是读取阶段。通常关注两个指标:time_to_first_token(首次返回 token 的时间,含排队和预填充)以及 waiting_requests 数量。若 time_to_first_token 已经很大,说明延迟在服务端;若客户端等待时间远大于该值,需要检查网络缓冲、连接池或代理层。

用 curl 或 Python 请求对比服务端记录的时间最简单。curl 加 -w 输出 time_starttransfer,再对应 vLLM 日志中该请求的首次 token 时间,两者的差近似网络与调度前排队开销。不要只看错误率,要看具体落在客户端超时之前多少比例的请求接近上限。

vLLM高并发推理时客户端超时与服务端scheduler delay的联调设定

服务端 scheduler delay 的常见成因与相应调整

scheduler delay 升高,说明请求进入等待队列后,调度器无法立刻把它放进 running batch。典型原因有三个:一是 batch 已满,即超过 max_num_seqs;二是 token 预算已满,即本批次可处理的 token 数到限;三是长 prefill 请求占住 batch,后面的短请求只能等待。vLLM 启动参数中,影响这三个现象的主要是 `--max-num-seqs` 与 `--max-num-batched-tokens`,以及可选开启的 chunked prefill 延长模式。启动前先 vllm serve `--help` 确认当前版本参数写法。

vllm serve /path/to/model \
    `--max-num-seqs` 80 \
    `--max-num-batched-tokens` 4096 \
    `--enable-chunked-prefill`

参数调整方向要看瓶颈在哪。若 waiting 队列有请求而 running 数量长时间顶满,说明 max_num_seqs 偏低,可以在显存允许范围内上调;若 GPU 利用率不低但请求首 token 延迟大,可能 token 预算太高导致单步前向时间过长,此时下调 `--max-num-batched-tokens` 或把长 prefill 拆成 chunked 模式。修改后观察同一并发下的 TTFT 分位数变化,而不是只观察平均时延。不要一边调大 batch,一边把客户端超时也调大,来掩盖每次请求被长时间排队造成的极端延迟。

vLLM高并发推理时客户端超时与服务端scheduler delay的联调设定

客户端超时与重试预算怎么给

客户端超时设置不应该来自“觉得 60 秒足够”,而是由服务端在目标并发下的 p95/p99 首 token 时间反推。做法是:先在当前服务参数下压测,记录 TTFT 的分位值。假设 p95 TTFT 为 8 秒,客户端读取超时给到 30 秒通常足够;如果业务要求 5 秒内返回,那就应该回改服务端参数,而不是提高客户端超时。超时上限还应叠加 SDK 重试逻辑,重试一次等于双倍请求量,在高并发下会加剧服务端排队,形成超时-重试-再超时的循环。

以 OpenAI 兼容客户端为例,timeout 与 max_retries 要一起调整,二者不要分别拍脑袋定。这里给出一个“先关闭重试再压测”的骨架:

vLLM高并发推理时客户端超时与服务端scheduler delay的联调设定
from openai import OpenAI

client = OpenAI(
    base_url="http://your-vllm-server:8000/v1",
    api_key="unused",
    timeout=30.0,
    max_retries=0,
)

先关闭自动重试,压测得到可接受的最长等待时间,再设置 timeout。若仍需重试,把 max_retries 设为 1,且重试只应覆盖连接中断,不应覆盖业务返回 error 的慢请求。在线服务的超时设定通常要大于服务端 p95 的 2 倍,小于请求内部业务断言的时限。

联调排查顺序

  1. 确认请求发到 vLLM 后,服务端日志中是否有排队信息,如 waiting/running/swapped 计数变化。
  2. 从 /metrics 拉取 time_to_first_token 与等待队列长度,确认 schedule delay 是否集中在排队段。
  3. 按 50、100、150 的方式逐步增加并发数,记录 TTFT 的 p50 与 p95。队列满时 p95 会先变坏,不要被平均 latency 骗过。
  4. 调整 max-num-seqs 或 max-num-batched-tokens 后,重新观察 p95,而不是只看错误码。
  5. 最后根据服务端 p95 核定客户端 timeout,开启重试时另算等待预算。

如果以上调整均无法降低 scheduler delay,可以先检查 vLLM 所在节点的 CPU 核数、GPU 占用与显存交换状态,确认不是调度线程自身被打满。此时需要的是部署侧的负载拆分或换更大显存实例,而不是继续堆 scheduler 参数。