
# 资源管理

Queuety 将资源控制分为四个核心层，以及可选的池大小调整层。

第一层描述单个工作单位的开销。第二层限制一个队列或提供商组一次可承受的共享压力。第三层定义单次工作流运行可消耗的资源上限。第四层决定工作进程是否应立即开始处理下一个已领取的作业，还是等到当前进程和容器有足够余量后再处理。

这些层协同工作。处理器可以声明自己属于某个高开销共享池；队列可以限制为某个提供商预留的加权工作量；工作流可以限制其愿意创建的此类工作量；即使当前进程或容器已接近资源上限，工作进程仍可以拒绝开始更多此类工作。

## 描述单个工作单位的成本

作业、工作流步骤和状态进入操作都可以发布相同的三项资源提示：

- `concurrency_group`
- `concurrency_limit`
- `cost_units`

`concurrency_group` 和 `concurrency_limit` 描述对某个下游系统造成的共享压力。如果多个处理器都访问同一个提供商、API 账户或租户，它们可以使用相同的组，这样工作进程就不会同时启动过多任务。

`cost_units` 描述相对权重。在工作流预算或工作进程准入规则判断是否可以安全地继续处理时，缓存刷新和大型提供商调用虽然都可能是“一个作业”，但不应被视为同等开销。

```php
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` 设置，因此彼此无关的处理器也可以共用同一个资源范围。

```php
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 为此提供了工作流级别的限制：

```php
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`。如果没有可靠的系统级快照，工作进程仍会继续使用进程本地检查。

## 扩展工作进程池，同时兼顾容量

固定工作进程池易于理解，但这也迫使你为闲时和流量高峰选定同一个数量。

自适应工作进程池则允许你分别设置下限和上限：

```bash
wp queuety work --queue=providers --min-workers=2 --max-workers=6
```

父进程会确保至少有最少数量的子进程保持运行；可领取的待处理任务增加时，子进程数量会逐步增至上限；队列在配置的空闲宽限期内保持安静后，数量则会缩减。

扩容仍会考虑可用容量。若启用了系统内存感知，且父进程看不到足以容纳另一个完整工作进程资源范围的剩余内存，它会维持当前池大小，而不会启动更多子进程。

## 实际组合各层控制

设想一个研究工作流：它会规划提供方调用、启动专用工作流，然后综合结果。

面向提供方的步骤可以发布 `cost_units` 和共享的 `concurrency_group`，以避免多个工作进程同时涌向同一个模型提供方。队列可以为该提供方流量预留加权预算。父工作流可以限制 `max_cost_units()` 和 `max_started_workflows()`，防止规划器无限扩展运行。随后，如果近期峰值内存数据显示进程或容器已非常接近上限，工作进程仍可延后新领取的提供方调用。

这样就有了四种彼此独立的控制方式：

1. 描述工作的权重
2. 预留共享队列或提供方容量
3. 限制单次运行可以创建的内容
4. 防止繁忙的工作进程启动过多任务

## 详细 API 所在位置

本页是概念概览。

具体配置项见：

- [工作进程](https://queuety.inline0.dev/zh/docs/features/workers)介绍准入行为和运行时常量
- [CLI](https://queuety.inline0.dev/zh/docs/cli)介绍固定和自适应工作进程池命令
- [工作流护栏](https://queuety.inline0.dev/zh/docs/workflows/guardrails)介绍工作流预算
- [任务](https://queuety.inline0.dev/zh/docs/jobs)介绍任务和处理程序元数据
- [状态机](https://queuety.inline0.dev/zh/docs/state-machines)介绍状态操作的资源元数据
- [API](https://queuety.inline0.dev/zh/docs/api)列出具体方法及其返回结构
