资源管理
Queuety 将资源控制分为四个核心层,以及可选的池大小调整层。
第一层描述单个工作单位的开销。第二层限制一个队列或提供商组一次可承受的共享压力。第三层定义单次工作流运行可消耗的资源上限。第四层决定工作进程是否应立即开始处理下一个已领取的作业,还是等到当前进程和容器有足够余量后再处理。
这些层协同工作。处理器可以声明自己属于某个高开销共享池;队列可以限制为某个提供商预留的加权工作量;工作流可以限制其愿意创建的此类工作量;即使当前进程或容器已接近资源上限,工作进程仍可以拒绝开始更多此类工作。
描述单个工作单位的成本
作业、工作流步骤和状态进入操作都可以发布相同的三项资源提示:
concurrency_groupconcurrency_limitcost_units
concurrency_group 和 concurrency_limit 描述对某个下游系统造成的共享压力。如果多个处理器都访问同一个提供商、API 账户或租户,它们可以使用相同的组,这样工作进程就不会同时启动过多任务。
cost_units 描述相对权重。在工作流预算或工作进程准入规则判断是否可以安全地继续处理时,缓存刷新和大型提供商调用虽然都可能是“一个作业”,但不应被视为同等开销。
final class CallProviderStep implements \Queuety\Step
{
public function handle( array $state ): array
{
// ...
}
public function config(): array
{
return [
'concurrency_group' => 'providers',
'concurrency_limit' => 3,
'cost_units' => 5,
];
}
}
同样的概念也适用于通过类属性分发的作业,以及通过 PendingJob::concurrency_group() 和 PendingJob::cost_units() 分发的单个作业。
为共享队列和提供商组设置预算
共享压力并不总是简单的并发数。有时,开销来自当前运行任务的总权重。
Queuety 通过队列和组的加权预算支持这种场景。队列预算按队列名称设置。组预算按 concurrency_group 设置,因此彼此无关的处理器也可以共用同一个资源范围。
define( 'QUEUETY_RESOURCE_QUEUE_COST_BUDGETS', [
'providers' => 20,
'exports' => 8,
] );
define( 'QUEUETY_RESOURCE_GROUP_COST_BUDGETS', [
'openai' => 12,
'tenant:acme' => 6,
] );
工作进程领取作业时,会检查属于该队列或组的活动 processing 行。如果当前运行任务的成本加上已领取作业的 cost_units 超出配置的预算,工作进程就会释放该作业,稍后再试。
这提供了不同于 concurrency_limit 的控制方式。提供商可以允许三个并发请求,同时为大型请求预留的总成本单位少于小型请求。
将单个工作流限制在预算范围内
当问题不在于某个繁重的作业,而在于一次运行不断发现超出预期的新工作时,就可以使用工作流预算。
这种情况在智能体系统中很常见。规划器可能反复访问同一个高成本分支,将任务拆分成过多的子任务,或者不断启动子工作流,却仍未超出常规重试模型的范围。
Queuety 为此提供了工作流级别的限制:
Queuety::workflow( 'research_run' )
->max_transitions( 20 )
->max_for_each_items( 12 )
->max_state_bytes( 32768 )
->max_cost_units( 40 )
->max_started_workflows( 8 )
->then( PlanResearchStep::class )
->for_each( 'tasks', ExecuteResearchTask::class, 'results' )
->dispatch();
max_cost_units() 可防止运行消耗超出预期的加权工作量。max_started_workflows() 可防止父工作流扩展为无界的顶层子运行树。其他防护措施仍然重要,因为失控的工作流并非都以相同方式造成高开销。
工作流状态会通过公开的 budget 负载展示已配置的限制和当前用量,因此这些限制生效期间仍可检查运行状态。
让工作进程在进程负载过高时延后处理任务
即使元数据和工作流预算配置得当,最终决定权仍属于即将执行任务的工作进程。
这正是资源感知准入发挥作用的地方。在已领取的任务开始前,Queuety 可以检查:
- 任务的共享并发组
- 加权队列和提供方组预算
- 处理程序近期的运行时长和峰值内存历史
- 当前工作进程剩余的内存余量
- 当前容器或主机剩余的内存余量
- 单次运行模式剩余的时间范围
如果工作进程已非常接近配置的上限,Queuety 会释放该任务,而不是启动很可能无法顺利完成的工作。
这是有意采取的保守策略。它并非完整的集群调度器,而是一个安全层,可帮助繁忙的工作进程避免超额占用其运行所在进程的资源;在内存遥测数据可用时,也能避免超额占用周围的容器或主机资源。
容器或主机级检查是可选的。启用 QUEUETY_RESOURCE_SYSTEM_MEMORY_AWARENESS 后,Queuety 会先尝试读取 cgroup 内存数据,并在 Linux 主机上回退到 /proc/meminfo。如果没有可靠的系统级快照,工作进程仍会继续使用进程本地检查。
扩展工作进程池,同时兼顾容量
固定工作进程池易于理解,但这也迫使你为闲时和流量高峰选定同一个数量。
自适应工作进程池则允许你分别设置下限和上限:
wp queuety work --queue=providers --min-workers=2 --max-workers=6
父进程会确保至少有最少数量的子进程保持运行;可领取的待处理任务增加时,子进程数量会逐步增至上限;队列在配置的空闲宽限期内保持安静后,数量则会缩减。
扩容仍会考虑可用容量。若启用了系统内存感知,且父进程看不到足以容纳另一个完整工作进程资源范围的剩余内存,它会维持当前池大小,而不会启动更多子进程。
实际组合各层控制
设想一个研究工作流:它会规划提供方调用、启动专用工作流,然后综合结果。
面向提供方的步骤可以发布 cost_units 和共享的 concurrency_group,以避免多个工作进程同时涌向同一个模型提供方。队列可以为该提供方流量预留加权预算。父工作流可以限制 max_cost_units() 和 max_started_workflows(),防止规划器无限扩展运行。随后,如果近期峰值内存数据显示进程或容器已非常接近上限,工作进程仍可延后新领取的提供方调用。
这样就有了四种彼此独立的控制方式:
- 描述工作的权重
- 预留共享队列或提供方容量
- 限制单次运行可以创建的内容
- 防止繁忙的工作进程启动过多任务
详细 API 所在位置
本页是概念概览。
具体配置项见: