news 2026/9/12 10:14:37

Onyx 如何用 stepramp 拐点爬升压测定位聊天服务容量上限

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Onyx 如何用 stepramp 拐点爬升压测定位聊天服务容量上限

Onyx 如何用 stepramp 拐点爬升压测定位聊天服务容量上限

【免费下载链接】danswerOpen Source AI Platform - AI Chat with advanced features that works with every LLM项目地址: https://gitcode.com/GitHub_Trending/da/danswer

聊天服务(Onyx,danswer 仓库)在做容量规划时,问题往往不是“固定 100 并发能不能跑”,而是“用户数加到多少时系统开始跟不上”。仓库tools/loadtest/内置了一套基于 Locust 的压测装置,按每个回合的里程碑(首包、首个 token、整回合耗时)记录延迟,并且提供一个可选的ONYX_SHAPE=stepramp拐点爬升形状:把并发用户数按 25 → 50 → 100 → 200 这样的台阶逐级推高,让系统“停止跟得上”的那个拐点直接出现在时间线上,而不是靠猜一个固定并发数。本文的操作路径是:装好 loadtest 依赖 → 接入确定性的 mock LLM → 创建 API key → 执行一次 stepramp 爬升 → 用 Prometheus + Grafana 叠图读出拐点。前提是你有一个可被压测的 Onyx 实例,并有管理员权限配置 LLM provider 与 API key。

准备:安装 loadtest 依赖并接入 mock LLM

Locust 及其 gevent/flask 依赖树放在根pyproject.toml的可选loadtest依赖组里(locust>=2.32等),默认不同步。在仓库根目录执行:

uv sync --group loadtest cd tools/loadtest

之后所有命令都要带--group loadtest,否则裸uv run会重新同步到默认组、把 Locust 丢掉(见 tools/loadtest/README.md 的 Setup 说明)。

压测装置的核心原则是:只测 Onyx 的应用代码与基础设施在压力下的表现,从不测 LLM 回答质量。因此 LLM 用仓库自带的 mock server 提供无限、零成本、确定性的调用量,任何回归都能归因到 Onyx 代码:

uv run --group loadtest uvicorn mock_llm.app:app --port 8001

然后把这个 mock 注册进 Onyx(Admin Panel → LLM,或PUT /api/admin/llm/provider):

  • provider 类型必须选openai_compatible,而不是openai—— litellm 会把openai类型路由到 OpenAI Responses API 桥,mock 没有实现该接口;
  • api_base指向 mock server(如http://localhost:8001),api_key 任意;
  • 为将要使用的模型名创建 model configuration。本场景按 README 建议配慢速推理模型mock-ttft8000-itl40-len600(长静默 TTFT),慢 provider 会让流保持打开更久,正是这种压力更早暴露 api-server 的连接/内存耗尽问题;
  • 每个 model configuration 的max input tokens 需 ≥ 50,000,否则默认窗口过低(deep research 场景会直接拒绝低于该值的模型)。

mock 的行为旋钮就写在模型名里(litellm 原样透传):ttft<ms>首 token 时间、itl<ms>token 间隔、len<n>回答长度,例如mock-ttft8000-itl40-len600就是 TTFT 8 秒、token 间隔 40ms、600 token 的回答。这些名字不是真实模型,必须先在 Onyx 注册为 model configuration 才能使用。

创建压测用的 API key

所有请求用ONYX_API_KEY作为 Bearer token。由管理员通过 Admin Panel → API Keys 创建,或调用POST /api/admin/api-key并传{"name": "loadtest", "role": "basic"}。下文命令中的<key>即指这把 key。

配置并执行 stepramp 拐点爬升

爬升形状实现于 shapes.py 的StepRampShape:把用户数依次压在ONYX_RAMP_STAGES给出的各个平台上,每个平台停留ONYX_RAMP_DWELL秒,超过最后一个平台后测试停止。locustfile.py 只在检测到ONYX_SHAPE=stepramp时才绑定这个形状 —— 因为 Locust 会自动激活它发现的任何 shape 并覆盖-u/-r,所以不设这个环境变量时爬升不生效。相关参数(均可省略,默认值如下):

变量默认值用途
ONYX_RAMP_STAGES25,50,100,200各平台的用户数,逗号分隔
ONYX_RAMP_DWELL300每个平台停留秒数
ONYX_RAMP_SPAWN5用户生成速率

README 给出的完整执行命令:

ONYX_SHAPE=stepramp ONYX_RAMP_STAGES=25,50,100,200 ONYX_RAMP_DWELL=300 \ ONYX_LLM_MODEL=mock-ttft8000-itl40-len600 \ ONYX_API_KEY=<key> uv run --group loadtest locust --headless -t 25m -H https://<your-onyx-url>

需要注意两点:

  • 形状激活后-u/-r会被覆盖,因此命令里不再给-u/-r
  • <your-onyx-url>必须是能服务浏览器等价路径的地址,即把/api/*路由到 api server 的 URL —— 实践中就是部署的用户访问入口(ingress / 负载均衡器)。按 k8s/locust.yaml 的注释,走 ingress 还会顺带检验 LB 空闲超时这类长流杀手。

不指定任何 user class 时跑的是默认加权混合流量:BasicChat 70 / ChatWithSearch 20 / MultiTool 8 / DeepResearch 2,近似生产流量形态;其中 ChatWithSearch 依赖已索引的文档(会走 query 扩展、embedding model server 和 Vespa/OpenSearch 检索)。4 个平台 × 每平台 300 秒约 20 分钟爬升,所以示例用-t 25m兜住整轮。

用 Prometheus 与 Grafana 读出拐点

每个聊天回合会在里程碑包到达的瞬间触发命名伪请求<scenario>:<milestone>,Locust 按名字聚合分位数。与定位容量上限最相关的几个:

  • chat:first_packet—— 流的第一行(服务端已接受并开始工作);
  • chat:first_answer_token—— 首个回答内容,即 TTFT;
  • chat:total_turn—— 整回合墙钟时间,成功/失败在这里记录

回合在以下情况判为失败:非 200、流中出现错误包、流在读超时(ONYX_STREAM_READ_TIMEOUT默认 180 秒,指 chunk 之间的间隔而非总时长)后仍无数据、或流在没有任何回答内容 / 没有stop包的情况下结束(截断)。

master/standalone 进程会在专用端口(默认9646LOCUST_PROMETHEUS_PORT可覆盖)暴露/metrics(实现见 prometheus_exporter.py),worker 不导出。可用指标:

  • locust_users
  • locust_requests_total{name,method}/locust_failures_total{name,method}
  • locust_response_time_p50_milliseconds/locust_response_time_p95_milliseconds{name,method}
  • locust_current_rps{name,method}

采集方式取决于你的 Prometheus:annotation-based 方案直接用 master pod 上的prometheus.io/scrape注解(已包含在 k8s/locust.yaml 中);Prometheus Operator(kube-prometheus-stack)忽略注解,需要 ServiceMonitor 指向metricsservice 端口 —— 该清单文件里附了注释掉的示例,release标签要改成与你的 Prometheus 的serviceMonitorSelector匹配。

最后一步是把指标叠成一张时间线:导入 tools/loadtest/dashboards/chat-loadtest-correlation.json,把里程碑延迟与失败率和服务器侧 CPU/内存画在同一时间轴上,并将 dashboard 的$namespace/$workload变量设为被压测的 deployment。README 对拐点的读法就是:p95 / 失败率开始上扬的那一档用户数,以及最先打满的资源。台阶式爬升的好处是每一档延迟水平都对应一个固定的用户数,拐点落在哪一档、被哪个资源卡住,叠图上一眼可见。

在目标集群内运行压测(可选)

如果目标是 k8s 部署,README 建议把整套装置跑进目标集群,避免 WAN 抖动污染延迟测量、LLM 调用保持免费:

kubectl apply -n <onyx-namespace> -f k8s/mock-llm.yaml kubectl create secret generic onyx-loadtest --from-literal=ONYX_API_KEY=<key> kubectl apply -n <onyx-namespace> -f k8s/locust.yaml kubectl port-forward svc/onyx-loadtest-master 8089:8089

然后从 8089 端口的 web UI 发起压测(host、用户数、scenario 均可在 UI 指定),或改 master 的 args 走 headless。注意:

  • mock 注册地址改为集群内的http://onyx-mock-llm:8000,并保持is_public=false、persona 作用域,避免真实用户看到它;
  • 大并发时扩onyx-loadtest-worker副本数(注释说明一个 worker 可扛数百条并发流),有条件就把 worker 钉在专用节点组上,别让压测生成器与被测系统抢资源;
  • 清单里的镜像占位<your-registry>/onyx-loadtest:v0.1.0要换成你自己构建的镜像(tools/loadtest/Dockerfile);分布式模式下所有 pod 必须跑同一构建,否则结果会被静默污染;
  • 形状与场景的环境变量(ONYX_SHAPEONYX_RAMP_STAGESONYX_LLM_MODEL等)通过 worker 清单的env传入,清单注释已注明这一配置入口并指向 README。

限制与注意点

  • README 明确:st-dev 集群是 direct-RDS,绝对数值与生产客户环境不同 —— 用这套结果对比不同配置之间的相对关系,不要拿绝对值当 SLA 承诺;
  • st-dev 环境下/loadtest子路径被 catch-all 路由遮蔽,LOCUST_HOST要指向专用 host 或用 port-forward;
  • LOCUST_HOST走 web/nginx host,健康探测路径用/api/health;直连 api Service 则用/health(这条主要针对 worker/threadpool 扫描场景,stepramp 主路径不涉及);
  • 这套压测衡量的是 Onyx 代码与基础设施,回答质量不在测量范围内 —— 这是装置设计上的边界,不是缺陷。

跑完一轮 stepramp 后,你能拿到的结论是文档定义的两个量:p95/失败率开始上扬的用户数档位,以及最先饱和的服务器资源。前者就是这次配置下的容量拐点,后者指向上一步该调的方向(worker 数、线程池、资源上限等,README 的 worker concurrency sweep 一节有对应的扫描方法)。

【免费下载链接】danswerOpen Source AI Platform - AI Chat with advanced features that works with every LLM项目地址: https://gitcode.com/GitHub_Trending/da/danswer

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

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

2026年学术写作工具测评与AI检测规避策略

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

作者头像 李华
网站建设 2026/9/12 10:12:10

微信小程序语音合成(TTS)开发实战与优化技巧

1. 微信小程序语音合成技术概述微信小程序的语音合成&#xff08;Text-to-Speech, TTS&#xff09;功能正在成为提升用户体验的重要技术手段。作为开发者&#xff0c;我们经常需要在教育类、导航类、内容阅读类小程序中集成语音播报功能。微信原生API虽然提供了基础的语音接口&…

作者头像 李华
网站建设 2026/9/12 10:11:20

Storm实时流处理与多数据源合并实战解析

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

作者头像 李华
网站建设 2026/9/12 10:10:16

双Y轴螺丝机自动化改造:PLC控制与机械手优化实践

1. 项目背景与核心需求去年在东莞某电子厂实施的双头双Y螺丝机自动化改造项目&#xff0c;本质上是为了解决传统螺丝锁附工序的效率瓶颈问题。这个电子厂主要生产智能家居控制面板&#xff0c;每块面板需要锁附12颗M2.5规格的螺丝&#xff0c;原先采用单工位人工操作时&#xf…

作者头像 李华
网站建设 2026/9/12 10:10:01

PHP单例模式为什么要禁止反序列化实例 ?

为什么要禁止反序列化实例&#xff1f;在PHP的单例模式中&#xff0c;禁止反序列化实例是为了确保单例的唯一性。当对象被序列化后&#xff0c;理论上它可以在其他地方被反序列化&#xff0c;从而可能创建出单例类的多个实例&#xff0c;这就违背了单例模式的设计初衷。通过禁止…

作者头像 李华
网站建设 2026/9/12 10:08:38

WeChatMsg:微信聊天记录本地导出 HTML、Word、CSV 与年度报告

WeChatMsg&#xff1a;微信聊天记录本地导出 HTML、Word、CSV 与年度报告 【免费下载链接】WeChatMsg 提取微信聊天记录&#xff0c;将其导出成HTML、Word、CSV文档永久保存&#xff0c;对聊天记录进行分析生成年度聊天报告 项目地址: https://gitcode.com/GitHub_Trending/w…

作者头像 李华