跳转到正文

持久化延迟

工作流可以使用构建器方法 delay() 在步骤之间暂停一段固定时长。延迟步骤具有持久性:延迟会存储在数据库中,因此 worker 重启、部署或服务器重新启动后仍会保留。

->delay() 方法

在工作流链中的任意位置添加延迟步骤:

PHP
use Queuety\Queuety;

Queuety::workflow( 'drip_campaign' )
    ->then( SendWelcomeEmailHandler::class )
    ->delay( days: 1 )
    ->then( SendFollowUpEmailHandler::class )
    ->delay( days: 3 )
    ->then( SendFinalEmailHandler::class )
    ->dispatch( [ 'user_id' => 42 ] );

工作流将会:

  1. 运行 SendWelcomeEmailHandler
  2. 等待 1 天
  3. 运行 SendFollowUpEmailHandler
  4. 等待 3 天
  5. 运行 SendFinalEmailHandler

时长参数

delay() 方法接受任意组合的命名时长参数:

参数 类型 说明
$seconds int 等待秒数
$minutes int 等待分钟数
$hours int 等待小时数
$days int 等待天数

所有值会相加。以下写法等效:

PHP
->delay( seconds: 3600 )
->delay( minutes: 60 )
->delay( hours: 1 )

你可以将它们组合使用:

PHP
->delay( hours: 1, minutes: 30 ) // 90 minutes total

延迟步骤的工作方式

工作流到达延迟步骤时,Queuety 会分派一个内部 __queuety_delay 作业,并将其 available_at 列设为当前时间加上延迟时长。该作业会留在数据库中,直到延迟时间结束后,才会被 worker 领取。

这意味着:

  • 无需轮询。 Worker 不会反复检查延迟。时间一到,作业就会自动变为可用。
  • 不占用内存。 等待期间没有任何进程在运行。延迟仅作为数据库中的一行记录存在。
  • 重启后仍然有效。 即使 worker 重启,延迟作业仍会留在队列中,并保留正确的 available_at 时间戳。

延迟结束后,延迟作业一旦被领取,工作流就会自动进入下一步。

示例:延迟通知

用户注册 1 小时后发送提醒,但前提是用户尚未完成新手引导:

PHP
Queuety::workflow( 'onboarding_reminder' )
    ->then( CreateAccountHandler::class )
    ->delay( hours: 1 )
    ->then( CheckOnboardingHandler::class )
    ->then( SendReminderHandler::class )
    ->dispatch( [ 'email' => 'user@example.com' ] );

CheckOnboardingHandler 步骤可以检查状态,并使用 条件分支,在新手引导已完成时跳过提醒。

示例:带退避的重试

在类似重试的步骤之间插入有意设置的延迟:

PHP
Queuety::workflow( 'poll_external_api' )
    ->then( CheckStatusHandler::class, 'check' )
    ->delay( minutes: 5 )
    ->then( CheckStatusHandler::class, 'recheck' )
    ->delay( minutes: 15 )
    ->then( CheckStatusHandler::class, 'final_check' )
    ->then( ProcessResultHandler::class )
    ->dispatch( [ 'external_id' => 'abc-123' ] );

多个延迟

一个工作流可以包含任意数量的延迟步骤。每个延迟都相互独立,并在工作流状态中通过顺序名称(delay_0、delay_1 等)进行跟踪。

PHP
Queuety::workflow( 'multi_step_drip' )
    ->then( StepA::class )
    ->delay( hours: 2 )
    ->then( StepB::class )
    ->delay( days: 1 )
    ->then( StepC::class )
    ->delay( minutes: 30 )
    ->then( StepD::class )
    ->dispatch( $payload );