每供应商 QPS
概述
每供应商 QPS 用于限制应用向某个 OCR、视频或其他异步服务商创建任务的速度。例如供应商规定“每秒最多创建 5 个任务”,就可以把该供应商的 QPS 设置为 5。超过速度的任务不会失败,而是继续保留在队列中,等待后续 Worker 扫描。
这个能力解决的是“单位时间内提交太快”的问题。它控制提交频率,不控制供应商当前正在执行多少个任务。
所有
manager.submit()创建的任务都会先进入队列。配置准入策略后,Worker 在创建供应商任务前统一执行 QPS 控制。
为什么需要 QPS 控制
没有统一限速时,一次批量导入可能瞬间向供应商发送数百个请求,常见后果包括:
- 触发供应商
429 Too Many Requests或封禁策略。 - 大量失败请求进入重试,进一步放大流量。
- 一个业务高峰占满供应商额度,影响其他正常请求。
- 不同业务各自限流,规则分散且难以统一调整。
把任务先写入 Store,再由 Worker 按 QPS 提交,可以把突发流量转换为平稳流量。
适用场景
- 供应商文档明确规定每秒请求上限。
- OCR 批量导入、视频批量生成等突发任务。
- 多个业务共用一个供应商,希望统一控制总提交速度。
- 供应商近期不稳定,需要临时降低请求速率。
如果限制是“同一个账号最多同时运行 2 个任务”,应使用每账号并发上限,而不是 QPS。
快速开始
1. 创建准入策略
import com.agentsflex.asynctask.policy.InMemoryAsyncTaskAdmissionPolicy;
InMemoryAsyncTaskAdmissionPolicy admission =
new InMemoryAsyncTaskAdmissionPolicy();
// Gitee 每秒最多提交 5 个任务
admission.setProviderQps("gitee", 5);2. 给任务设置相同的供应商键
import com.agentsflex.asynctask.AsyncTaskOptions;
AsyncTaskOptions options = new AsyncTaskOptions();
options.setProviderKey("gitee");
AsyncTask task = manager.submit(
OcrRequest.ofUrl("https://files.example.com/document.pdf"),
30 * 60_000L,
options
);请求参数必须适合持久化;OCR 应使用远程 URL,不能提交本地文件。providerKey 未设置时会使用 Handler Key;准入策略中的键必须与任务最终保存的键完全一致。
3. 将策略交给 Worker
AsyncTaskWorker worker = new AsyncTaskWorker(
"worker-1",
store,
registry,
retryPolicy,
admission,
30_000L
);
worker.start(200L, 20);至此,即使业务瞬间调用 submit() 创建 100 个任务,Worker 也只会按配置速度提交,其他任务保持 PENDING_SUBMIT。
工作原理
InMemoryAsyncTaskAdmissionPolicy 为每个 providerKey 保存最近 1000 毫秒内获准提交的时间点,窗口定义为 (now - 1000, now]。
QPS 为 2 时:
1000ms:任务 A、B 获准提交
1500ms:任务 C 仍在队列中
2000ms:A、B 离开统计窗口,任务 C 获准提交供应商暂停、租户配额和账号并发会先于 QPS 判断。只有最终获准提交的任务才消耗 QPS 额度。
如何选择 QPS
优先使用供应商官方额度,并预留一定余量。例如官方上限是 10 QPS,可以先配置为 8,观察 429、网络延迟和成功率后再调整。
Worker 的扫描间隔和批量大小也会影响实际速度:
batchSize小于 QPS 时,单轮无法用满额度。- 扫描间隔过长时,实际吞吐可能低于上限。
- QPS 是最大允许值,不是每秒必须达到的目标值。
与其他能力组合
常见生产配置会同时限制提交速度和活动任务数:
admission.setProviderQps("gitee", 5);
admission.setAccountConcurrency("gitee", "account-a", 2);任务必须同时满足两项条件:当前一秒还有 QPS 额度,并且账号仍有并发容量。
集群部署
InMemoryAsyncTaskAdmissionPolicy 的窗口只在当前 JVM 中有效。3 个进程都配置 5 QPS 时,集群总速度可能接近 15 QPS。
需要严格全局 QPS 时,应实现基于 Redis、数据库或网关的共享 AsyncTaskAdmissionPolicy,通过原子操作维护滑动窗口;另一种方案是让同一供应商只有一个 Worker 负责提交。
QPS 与账号/租户容量的实现方式不同:内置 QPS 会在策略对象内部记录本轮获准次数,因此同一策略实例的 批量领取仍受窗口限制;账号并发和租户配额依赖任务快照,使用 JDBC/Redis 时需要额外的原子预占。
常见问题
被限流的任务会失败吗?
不会。任务继续保持 PENDING_SUBMIT,下一轮扫描会重新尝试,直到获准、取消或超过跟踪截止时间。
QPS 会限制结果查询吗?
不会。当前能力只限制供应商任务创建,已经提交任务的 query() 调用不计入该 QPS。
为什么配置后没有生效?
确认使用的是 submit(),Worker 构造器传入了该 admission 实例,并且任务的 providerKey 与配置键一致。