hyperf异步任务重试需显式配置三类环节:投递失败由retry_seconds控制,消费失败须在handle()中手动调用retry()或release(),且重试策略受实例级、类级、全局级三层叠加控制。

Hyperf异步任务消费失败后的重试,不是“配了就自动生效”的黑盒机制,而是由多个环节协同控制的显式流程。关键在于区分清楚:谁在重试?什么时候重试?按什么规则重试?漏掉任一环,任务就会静默失败或直接进 failed 队列不再触发。
重试行为分两类,必须分开配置
很多人以为 retry_seconds 一设,所有失败都会自动重试——这是最大误区。Hyperf 中实际存在两种独立的重试路径:
-
投递失败重试:指任务从 PHP 进程 push 到 Redis 队列时失败(如 Redis 连接超时),此时由框架底层自动重试,受 config/autoload/async_queue.php 中
retry_seconds控制; -
消费执行失败重试:指 Consumer 进程已拉取任务、执行
handle()时抛出异常,此时框架默认不自动重试,而是记录日志 + 移入 failed 队列——除非你在handle()里主动调用$this->retry(5)或$this->release(10)。
Job 类中必须手动接管执行失败逻辑
所有继承 Hyperf\AsyncQueue\Job 的任务类,handle() 方法内必须用 try/catch 包裹核心逻辑,并在捕获异常后明确决定是否重试:
- 临时性错误(如网络超时、DB 连接抖动)→ 调用
$this->retry(30)延迟 30 秒后重新入队; - 需降级处理的错误(如第三方接口限流)→ 调用
$this->release(60)延迟 60 秒再试; - 永久性错误(如参数非法、用户已注销)→ 直接
throw $e让其进 failed 队列,避免无限循环; - 切勿写空
catch,例如catch (\Exception $e) { },这会让框架误判为“执行成功”,重试机制完全失效。
重试次数与间隔的三层控制权
真正起作用的重试策略,是三个层级叠加的结果,优先级从高到低:
-
实例级:在
handle()中调用$this->retryAfter(120),可动态覆盖所有上层设置,适合根据错误码做差异化退避; -
类级:在 Job 类中定义
protected int $maxAttempts = 4;,表示该任务最多尝试 5 次(含首次); -
全局级:config/autoload/async_queue.php 中
default → retry_seconds => [1, 5, 15, 60],表示按指数退避依次等待,共重试 4 轮(对应最多执行 5 次)。
验证重试是否真实触发的实操方法
别只看配置,要亲眼确认重试链路跑通:
- 在
handle()开头加日志:file_put_contents('/tmp/queue.log', "run at " . date('H:i:s') . "\n", FILE_APPEND); - 故意抛异常:
if ($this->attempts() - 观察
/tmp/queue.log是否出现多条时间戳,间隔是否符合你配置的retry_seconds; - 同时用
redis-cli lrange hyperf:queue:default 0 -1查看任务是否被重新推入队列。











