news 2026/9/27 18:42:11

高并发压测实战:用 benchmark_serving.py 拆解 RPS 与 Token 生成率,TaoToken 统一 Key 接入 vLLM

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
高并发压测实战:用 benchmark_serving.py 拆解 RPS 与 Token 生成率,TaoToken 统一 Key 接入 vLLM

1. 为什么“感觉够用”总在流量高峰翻车

高并发压测这件事,我见过太多团队栽在同一个坑里:上线前拿单条请求测了测,延迟 300ms,觉得“性能不错”,结果生产环境并发一上来,TTFT 直接飙到两三秒,甚至服务直接 OOM。问题不在于模型不行,而在于没有用数据定义过系统极限。

vLLM 推理服务的性能观测,核心就两件事:RPS(每秒请求数)和Token 生成率(Tokens/s)。前者告诉你服务能扛多少流量,后者告诉你生成速度够不够快。而benchmark_serving.py就是 vLLM 自带的“压力测试机”,它能模拟真实用户请求流量,精确记录系统在重负载下的表现。

这篇内容聚焦 vLLM 推理服务在高并发压测下的性能观测,围绕benchmark_serving.py的完整压测流程,讲清 RPS 与 Token 生成率两项指标怎么采集、怎么解读、怎么定位瓶颈。同时给出 TaoToken 统一 Key/API 通道的settings.json配置骨架,让你在压测脚本里也能走统一入口,不用为每个环境单独维护一套 Key。适合正在做推理服务容量规划、或者被高并发延迟问题困扰的工程师。

2. TaoToken 前置:统一 Key 与 API 通道准备

压测脚本本身不复杂,麻烦的是多环境 Key 管理。本地测一套、测试环境一套、生产又一套,压测时还得手动切换 base_url,很容易搞混。TaoToken 的思路是提供一个统一的 API 通道,你只需要维护一个 Key,通过配置切换模型和端点。

先拿到你的 Key。访问控制台创建 API Key:

https://taotoken.net/console?utm_source=taotoken_aicg_blog_end&utm_content=console&utm_campaign=rewrite

创建完成后,Key 只在创建时显示一次,记得立刻保存。如果你还没决定用哪个模型做压测,可以先在模型对话页面确认可用模型列表:

https://taotoken.net/models?utm_source=taotoken_aicg_blog_end&utm_content=models&utm_campaign=rewrite

API 的基础地址是https://taotoken.net/api,这个地址不加任何 UTM 参数,直接用于代码里的base_url。接入文档在这里,里面有完整的请求格式和参数说明:

https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_content=doc&utm_campaign=rewrite

如果你打算长期跑压测和编码任务,Coding Plan 会比按量计费更划算,适合需要反复调参、多轮压测的场景:

https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_content=coding-plan&utm_campaign=rewrite

注意:压测会产生大量请求,建议先用小num-prompts跑通流程,确认 Key 和端点无误后再放大并发,避免不必要的消耗。

3. 可复制配置:settings.json 骨架与压测命令

3.1 settings.json 配置骨架

把 Key 和端点集中放在一个配置文件里,压测脚本读取它,避免硬编码。下面是一个可直接用的骨架:

{ "api": { "base_url": "https://taotoken.net/api", "api_key": "sk-your-key-here", "timeout": 120 }, "benchmark": { "backend": "openai", "model": "your-model-name", "dataset_name": "sharegpt", "num_prompts": 500, "request_rate": 10, "output_file": "result_baseline.json" }, "vllm_server": { "host": "localhost", "port": 8000, "max_num_seqs": 128 } }

这里backend填openai,因为 TaoToken 走的是 OpenAI 兼容协议,benchmark_serving.py能直接识别。base_url指向 TaoToken 的 API 地址,api_key填你刚创建的 Key。vllm_server段是给你本地 vLLM 服务用的,压测脚本会向这个地址发请求。

3.2 准备压测数据集

benchmark_serving.py需要一个包含真实 prompt 的数据集。最常用的是 ShareGPT 格式的 JSONL 文件,每行一个对话样本。如果你有线上日志,脱敏后抽取变长输入,压测结果会更贴近真实场景。没有的话,脚本支持--dataset-name sharegpt自动拉取公开数据集。

3.3 基线压测命令

先跑一个低并发基线,确认链路通畅:

python benchmark_serving.py \ --backend openai \ --base-url https://taotoken.net/api \ --model your-model-name \ --dataset-name sharegpt \ --num-prompts 500 \ --request-rate 10 \ --output-file result_baseline.json

这条命令以每秒 10 个请求的速率发送 500 个请求。--request-rate控制的是请求到达速率,不是并发数,但两者在压测中会相互影响。跑完后result_baseline.json里会有完整的指标数据。

3.4 多档位并发压测脚本

单点数据没有意义,要看趋势。写个 Shell 循环,自动递增并发档位并保存多组结果:

#!/bin/bash for rate in 10 20 40 60 80 100 150 200; do echo "Testing with request rate: $rate" python benchmark_serving.py \ --backend openai \ --base-url https://taotoken.net/api \ --model your-model-name \ --dataset-name sharegpt \ --num-prompts 1000 \ --request-rate $rate \ --output-file result_rate_${rate}.json sleep 5 done

每档之间sleep 5秒,让服务从上一轮压力中恢复,避免残留请求影响下一轮数据。num-prompts提到 1000,样本量更大,指标更稳定。

4. 验证请求与成功结果解读

4.1 确认请求真的发出去了

压测脚本跑起来后,第一件事是确认请求确实到达了服务端。在 vLLM 服务端日志里应该能看到连续的请求记录。如果日志里没有动静,说明base_url或api_key有问题,先排查配置再继续。

4.2 成功结果长什么样

一次正常的压测输出,终端会打印类似这样的汇总:

============ Serving Benchmark Result ============ Successful requests: 1000 Benchmark duration (s): 52.34 Total input tokens: 128450 Total generated tokens: 256800 Request throughput (req/s): 19.11 Output token throughput (tok/s): 4906.32 Total Token throughput (tok/s): 7360.15 ---------------Time to First Token---------------- Mean TTFT (ms): 186.42 Median TTFT (ms): 172.10 P99 TTFT (ms): 412.55 -----Time per Output Token (excl. 1st token)------ Mean TPOT (ms): 18.73 Median TPOT (ms): 17.95 P99 TPOT (ms): 31.20 ==================================================

这里几个关键字段:

字段含义关注点
Request throughputRPS,每秒完成请求数吞吐能力直接指标
Output token throughputToken 生成率生成速度,影响体验
Mean TTFT首字延迟均值用户感知的响应快慢
P99 TTFT首字延迟 99 分位长尾请求的糟糕程度
Mean TPOT每输出 Token 耗时生成阶段的稳定性

4.3 多档位数据对比

把各档位的result_rate_*.json汇总,重点看两个曲线的变化:

import json import glob results = [] for f in sorted(glob.glob("result_rate_*.json")): with open(f) as fp: data = json.load(fp) rate = int(f.split("_")[-1].replace(".json", "")) results.append({ "request_rate": rate, "rps": data["request_throughput"], "token_throughput": data["output_token_throughput"], "ttft_mean": data["mean_ttft_ms"], "ttft_p99": data["p99_ttft_ms"] }) for r in results: print(f"rate={r['request_rate']:>4} RPS={r['rps']:>7.2f} " f"Tok/s={r['token_throughput']:>8.2f} " f"TTFT_mean={r['ttft_mean']:>7.2f}ms TTFT_p99={r['ttft_p99']:>7.2f}ms")

输出大概是这样:

rate= 10 RPS= 9.87 Tok/s= 2534.10 TTFT_mean= 142.30ms TTFT_p99= 210.55ms rate= 20 RPS= 19.52 Tok/s= 5012.44 TTFT_mean= 158.72ms TTFT_p99= 245.10ms rate= 40 RPS= 38.11 Tok/s= 9786.20 TTFT_mean= 186.42ms TTFT_p99= 412.55ms rate= 60 RPS= 52.34 Tok/s=13440.80 TTFT_mean= 245.18ms TTFT_p99= 680.30ms rate= 80 RPS= 58.90 Tok/s=15120.55 TTFT_mean= 380.44ms TTFT_p99=1120.40ms rate= 100 RPS= 60.12 Tok/s=15430.20 TTFT_mean= 620.75ms TTFT_p99=1850.60ms rate= 150 RPS= 58.44 Tok/s=14980.10 TTFT_mean= 980.30ms TTFT_p99=3200.80ms rate= 200 RPS= 52.10 Tok/s=13320.60 TTFT_mean=1450.20ms TTFT_p99=5100.30ms

4.4 怎么读这张表

线性增长期:rate 从 10 到 40,RPS 基本线性上升,TTFT 保持稳定。说明系统资源充裕,GPU 算力还没跑满。

拐点出现:rate 到 60 以后,RPS 增长明显放缓,TTFT 开始抖动。这是显存带宽开始成为瓶颈的信号。大模型推理是典型的显存带宽敏感型任务,多个请求同时争夺 HBM 带宽读取权重和 KV Cache,延迟必然上升。

性能坍塌:rate 超过 100 后,RPS 不升反降,TTFT 急剧飙升。这通常是连续批处理调度开销过大,或者显存碎片化导致无法分配新 Block,触发频繁内存整理。

从上面数据看,这个服务的最佳工作点在 rate=60 到 80 之间:RPS 接近峰值,TTFT 还在可接受范围。超过 100 就是拿延迟换吞吐,不划算。

5. 本篇常见错排查

5.1 报错 Connection refused

压测脚本连不上服务。先确认 vLLM 服务是否在跑:

curl http://localhost:8000/health

返回{"status":"ok"}说明服务正常。如果连不上,检查 vLLM 启动命令里的--host和--port,默认是0.0.0.0:8000。用 TaoToken 通道时,确认base_url写的是https://taotoken.net/api,不要多加路径。

5.2 报错 401 Unauthorized

Key 不对或没带上。检查settings.json里的api_key是否完整,有没有多余空格。TaoToken 的 Key 以sk-开头,创建后只显示一次,如果丢了就重新创建一个。

5.3 RPS 上不去,但 GPU 利用率也不高

这种情况通常是请求速率没打满。--request-rate控制的是请求到达速率,如果设得太低,服务根本吃不满。逐步提高 rate,观察 RPS 是否跟着涨。如果 rate 提到很高 RPS 还是不动,检查--num-prompts是不是太小,请求发完了脚本就结束了。

5.4 TTFT 正常但 Token 生成率很低

说明首字响应快,但生成阶段慢。重点看 TPOT(每输出 Token 耗时)。如果 TPOT 很高,可能是--max-num-seqs设得太大,单个批次里序列太多,每个请求分到的计算资源被稀释。试着调小这个值:

vllm serve your-model \ --max-num-seqs 64 \ --host 0.0.0.0 \ --port 8000

低延迟优先场景(在线客服),建议设成显存容量的 1/4 到 1/3 对应的序列数。高吞吐优先场景(离线处理),可以保持默认或适当调大。

5.5 压测结果波动大,每次跑都不一样

正常现象,但波动太大说明有问题。检查几点:压测前服务是否空闲(上一轮残留请求会影响);num-prompts是否太小(样本不足);是否有其他进程占用 GPU。建议每档之间sleep足够时间,num-prompts至少 500 以上。

5.6 用 TaoToken 通道时超时

压测请求量大,默认超时可能不够。在settings.json里把timeout调大,比如 120 秒。如果还是超时,检查网络到taotoken.net的连通性,以及是否触发了速率限制。压测场景建议用 Coding Plan,配额更充裕。

6. 把压测数据变成容量规划依据

压测的最终目的是回答一个问题:在保证 TTFT < X ms 的前提下,单卡最大支持 Y RPS。有了这个数据,容量规划就是算术题。

假设业务峰值 1000 RPS,单卡在 TTFT < 500ms 约束下能撑 60 RPS,那么至少需要 17 张卡,考虑冗余部署 20 张。这种基于数据的规划,比拍脑袋靠谱得多。

几个实操建议。第一,压测数据集尽量用线上脱敏日志,变长输入才能模拟真实压力。第二,不要只看平均值,P99 TTFT 才是用户体验的底线。第三,每次调参后重新压测,max-num-seqs、gpu-memory-utilization这些参数对结果影响很大。第四,把压测脚本和settings.json纳入版本管理,换环境时只改配置不改代码。

如果你在压测中遇到接入问题,先看接入文档排查配置:

https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_content=doc&utm_campaign=rewrite

需要重新生成或管理 Key,去 API Keys 页面:

https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_content=api-keys&utm_campaign=rewrite

长期跑压测和编码任务,Coding Plan 的配额更合适:

https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_content=coding-plan&utm_campaign=rewrite

压测这件事没有银弹,只有反复测试和权衡。每一次参数调整,都以真实数据为准绳。摸清了系统极限,流量洪峰来了才能从容应对。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/27 18:38:12

JetBrains 全系列 IDE 接入 deepseekAI 模型:TaoToken 统一 Key 配置实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/27 18:34:23

A2A + MCP 实战:用 TaoToken 统一 Key 打通企业级 Multi-Agent 协议链路

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华