ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

上线之后怎么验收:延迟限流与异步索引

上线之后怎么验收:延迟限流与异步索引

上篇博客讲了怎么把个人项目挂到公网:浏览器能登录、能上传、能问答,算是把主链路跑通了。
这篇主要是讲上线后的工程质量验收:护栏会不会拒、会不会误伤;异步索引是不是真在跑;慢的时候大概慢在哪。例子还是用我的开源项目 DocPaws,在 2 核 2G 演示机上做一轮。

先搞清请求怎么走,再按两条链路验收:

  1. 问答侧:准备脚本 → 验限流 → 抽延迟
  2. 上传/索引侧:上传 → ready → 改一处找瓶颈

一、一台机上,请求怎么走

/api

开流前

上传文档

浏览器

Nginx

API

限流:每分钟次数 / 同时几路

模型流式回答

任务队列

Celery worker

向量 / 索引 ready

  • 问答:限流在真正开 SSE 之前就拦;拦不住才去调模型。
  • 上传:接口很快返回,真正建索引的是后台 worker;页面变成 ready,才算这条链路完成。

为什么非要拦在开流之前?因为一旦已经出字,模型调用、连接占用都花出去了,超配额的请求还可能已经拿到内容——护栏等于白装。拦在前面,才是省成本、让限流真管用。

DocPaws 里常见配置(在后端.env):

含义配置名(示例)演示机常用值
开不开限流CHAT_RATE_LIMIT_ENABLEDtrue
每分钟最多问几次CHAT_RATE_LIMIT_PER_MINUTE日常20;测拒绝时临时改5
同时最多几路问答CHAT_CONCURRENT_LIMIT默认常2

改完.env一定要重启 API(DocPaws 用 systemd 时:sudo systemctl restart docpaws-api)。

二、问答侧验收:限流与延迟

这一侧共用同一套东西:登录态 + 固定题 + 小脚本。先准备好,再验限流、再抽延迟。

1. 准备

  1. 浏览器走通:登录 → 小 PDF 索引ready→ 能流式问答。
  2. 开发者工具里抄三样:
    • Cookie 里的session(登录态)
    • 知识库kb_id
    • 一道固定问题

为了做这轮验收,我让 AI 写了个小脚本(仓库里在eval/bench_chat_stream.py),对同一道题反复打问答接口:记首包(多久开始出字)和整段耗时(多久答完),撞上限流就拿 HTTP 429(Too Many Requests,表示「你问太勤,被拒了」)。用--mode切换几种验收场景就行——baseline抽延迟,burst/pace验限流拒与不误杀,verify改回日常频率后再确认。

2. 验限流:该拒则拒,正常节奏不误杀

为什么先验限流?它像小区门禁:太松,闲杂人员随便进,模型调用费可能被刷;太紧,业主自己也被挡在外面。

所以要验两件事——它们其实是同一个机制的两个相反失败方向:

  • 拒绝(防漏):问太勤,该拦下来(常见是 HTTP 429)。漏了,护栏等于没装。
  • 误杀(防滥):按正常人的节奏问,不该被拦。滥了,用户用不了,这肯定也不行。
临时把「每分钟次数」调严

编辑后端.env,例如改成每分钟只允许 5 次,然后重启 API:

sudosystemctl restart docpaws-api
狂打:看会不会拒

本机:

python3 bench_chat_stream.py--modeburst

我在演示机上的结果(频率=5,连打 18 次):前5次成功,后面13次 429。
说明:超配额会被拦——护栏是活的。

然后仍保持频率=5,但每次隔十几秒再问:

python3 bench_chat_stream.py--modepace

我这边 8 次全成功、0 次 429。
说明节奏已经低于配额时,不会误伤。

改回日常频率,再验一遍

把.env改回例如20,重启 API,再跑:

python3 bench_chat_stream.py--modeverify

几乎没有 429。

场景频率结果(我的演示机)
狂打518 次里 13 次 429
慢打58 次全过,误杀 0
改回后再验2010 次全过

3. 抽一轮问答延迟

限流验完、频率回到日常后,再抽一轮体感基线——以后变慢了,才知道相对什么变了:

python3 bench_chat_stream.py--modebaseline

脚本串行问二三十次,打出首包和整段的分位。首包(TTFT):多久出现第一条data:(感觉开始出字)。整段(E2E):多久答完,大头多半在模型生成。次数不多,看 P50 量级即可,别当 SLA;串行也说明不了多人同时问会怎样。

在测试时,我发现前二十几次还正常,到大约第 24 次附近脚本报 HTTP 500/系统繁忙。这和限流 429 不是一类事:429 是护栏在拦;这里是池子里借不到连接了。API 日志里能看到类似:

sqlalchemy.exc.TimeoutError: QueuePool limit of size 5 overflow 10 reached, connection timed out, timeout 30.00

后面发现是问答路径把数据库连接握太久、没还干净——旧实现往往借一次就握到答完,等模型吐字也占着名额。串行都能打满,说明不是测太狠,是资源生命周期有问题。后来把后端改成短借短还,才能正常测完。

我这轮测的数据:25 次全过、无 429;首包中位约 42ms,整段中位约 11s。

三、上传/索引侧验收

1. 上传 → ready

为什么单独验这一条?上传接口返回「成功」和文档变成ready不是一回事——中间靠后台 worker。文件一直显示传输中,往往是异步链路没起来或任务挂了。

  1. SSH 确认 worker 在跑,上传前就挂上日志:
sudosystemctl status docpaws-workersudojournalctl-udocpaws-worker-f
  1. 浏览器传一个不用太大的 PDF(大约 1~2MB 就够了)。
  2. 看两样:
    • 页面/状态何时变成ready;
    • 日志里是否出现任务received→ 切块 / 向量 →succeeded。

可选再开一个窗口用htop瞄一眼内存 CPU。

我在 2C2G 上、约 2MB 样例:任务大约0.4~0.9 秒就成功,机器 Load 很低、内存大概用到 1G/1.57G 量级,没有明显积压。


2. 改一处再比:找瓶颈

只改一个配置项、同一路径前后对比,才能把快慢钉到具体原因上。我把 Celery--concurrency(同时能并行啃几条任务)从默认调到 4,再传差不多的小 PDF:

规模(chunks)改前改后
约 11≈0.98s≈0.75s

基本持平。一次只传一个文件时,加 worker 数往往缩短不了这一单,时间主要花在切块和 embedding(文本转向量)上。
从而结论:瓶颈不在 concurrency,而在任务内 / 外部向量接口。

四、进阶做法(可选)

同时开几路问答(并发租约)
「租约」可以理解成:开一路问答就占一个名额,答完(或超时)再还回来。DocPaws 除了「每分钟次数」,还有「同时最多几路」。默认常常是 2。
同一账号同时开 3 路:应有人拿到 429(错误码里常见是CONCURRENT_LIMITED「同时开太多路」,和分钟桶的RATE_LIMITED「这一分钟问太勤」不同)。
三路都结束后马上再问一次:应能通过——说明名额有释放。
若只有「等很久再试」才恢复,那测的是过期兜底,不是「结束就释放」。两件事别混。

Redis 挂了会怎样
Redis 在这里常被限流拿来记「你已经问了几次 / 占了几路」。短停 Redis 再问:理想是明确失败(例如服务暂不可用),而不是限流悄悄失效、人人狂打。

返回列表