动态 For-Each
如果构建时工作流尚不知道分支数量,请使用 ->for_each()。Queuety 会从工作流状态读取数组,为每个数组项调度一个分支作业,并且只有满足配置的完成条件后才会继续。
->for_each() 方法
use Queuety\Contracts\ForEachHandler;
use Queuety\Contracts\ForEachReducer;
use Queuety\Enums\ForEachMode;
use Queuety\Queuety;
Queuety::workflow( 'index_site' )
->then( PlanPagesStep::class )
->for_each(
items_key: 'pages',
handler_class: IndexPageStep::class,
result_key: 'page_results',
mode: ForEachMode::Quorum,
quorum: 2,
reducer_class: IndexSummaryReducer::class,
name: 'index_pages',
)
->then( PublishIndexStep::class )
->dispatch( [ 'site_id' => 42 ] );
规划器步骤会将 pages 写入工作流状态。For-Each 步骤读取该数组,并为每个数组项调度一个分支作业。
分支处理器
For-Each 分支处理器实现 Contracts\ForEachHandler:
use Queuety\Contracts\ForEachHandler;
final class IndexPageStep implements ForEachHandler {
public function handle_item( array $state, mixed $item, int $index ): array {
$page = is_array( $item ) ? $item : [ 'url' => (string) $item ];
return [
'url' => $page['url'],
'title' => fetch_title( $page['url'] ),
'index' => $index,
];
}
public function config(): array {
return [
'max_attempts' => 3,
'backoff' => 'exponential',
];
}
}
在 config() 中,backoff 接受一个策略名称:exponential、linear 或 fixed。其中的逐次尝试延迟列表目前会被丢弃并变成立即重试(#16);请改为在步骤定义的 backoff 中设置逐次尝试延迟。
$state 包含完整的工作流状态,$item 是单个 For-Each 项目,$index 是其从零开始的索引位置。
完成模式
ForEachMode::All
只有所有分支都成功后,该步骤才会完成。任何分支永久失败都会导致 For-Each 步骤失败。
ForEachMode::FirstSuccess
只要第一个分支成功,该步骤就会完成。其余仍在运行的分支会被标记为已取代并埋藏。
ForEachMode::Quorum
至少有 quorum 个分支成功时,该步骤就会完成。如果其余分支已无法满足法定人数要求,工作流就会失败。
归约器
默认情况下,Queuety 会在 result_key 下存储结构化汇总:
modequorumtotalsucceededfailedsettledwinnerresultsfailures
如果要生成自定义摘要,请传入实现 Contracts\ForEachReducer 的归约器类。汇总仍会存储在 result_key 下,归约器返回的键则会合并到工作流状态中,与其并列:
use Queuety\Contracts\ForEachReducer;
final class IndexSummaryReducer implements ForEachReducer {
public function reduce( array $state, array $for_each ): array {
return [
'indexed_count' => count( $for_each['results'] ),
'winning_url' => $for_each['winner']['output']['url'] ?? null,
];
}
}
归约器的输出会像其他步骤输出一样合并到工作流状态中。它也可以返回 _next_step,以便在 For-Each 完成后转到其他分支。
重试行为
For-Each 分支会根据分支处理器的 config() 独立重试。如果 For-Each 步骤失败后调用 retry_workflow(),Queuety 会重新展开该步骤,并且只将仍缺失或失败的分支重新加入队列。成功分支的结果会保留在工作流状态中。
选择 parallel() 还是 for_each()
如果分支列表在代码中固定,请使用 parallel()。
如果分支列表是在运行时从工作流状态中发现的,并且需要首个成功或法定人数等完成模式,请使用 for_each()。