Hyperf 3.1 中任务重试由 $maxAttempts 和 retry_seconds 共同控制,max_attempts=2 表示最多执行3次;可通过类属性、构造函数或 retryAfter() 动态配置,需避免 sleep 阻塞协程。

在 Hyperf 3.1 中实现订单超时关闭、邮件重发、支付结果补推等关键业务时,任务失败后必须自动重试,否则会导致用户余额不更新、订单状态卡死、通知丢失等线上事故。
理解重试机制的两个核心参数
重试行为由 【$maxAttempts】 和 【retry_seconds】 共同决定,缺一不可。前者控制“最多试几次”,后者控制“每次失败后隔多久再试”。
比如配置 max_attempts = 2、retry_seconds = 5,意味着:首次执行失败 → 等5秒 → 第二次执行 → 若再失败 → 等5秒 → 第三次执行 → 成功或彻底放弃(不再入队)。
注意:max_attempts 值为 2 时,实际最多执行 3 次(含首次),这是官方文档明确说明的语义,不是 bug。
在 Job 类中启用重试
方法一:通过类属性声明
在 app/Job/OrderCloseJob.php 中,直接定义 protected int $maxAttempts = 3;
这会覆盖 config/autoload/async_queue.php 中 default 配置的全局 max_attempts,优先级更高。该写法简洁明确,适合单任务定制化强的场景。
方法二:构造函数动态传入
在 new OrderCloseJob($params, 5) 时,把重试次数作为第二个参数传入基类 Job 的构造器——但前提是你的 Job 类没有重写 __construct(),否则需手动透传。
这个方式灵活,但容易因忘记调用 parent::__construct() 导致重试失效,【务必检查父类构造是否被正确调用】。
配置 retry_seconds 的三种生效位置
① 全局默认值:config/autoload/async_queue.php 中 default → retry_seconds = 10
② 驱动级覆盖:可为 RedisDriver 单独设 retry_seconds,适用于多队列不同策略
③ Job 实例级:在 handle() 方法内抛出异常前,调用 $this->retryAfter(30) —— 这会覆盖所有上层配置,精确到秒,适合根据错误类型动态调整重试间隔,例如数据库死锁立即重试,网络超时则延后60秒。
不要在 handle() 中用 sleep() 替代 retryAfter(),sleep 会阻塞协程,导致整个消费者进程卡住,其他任务全部堆积。
验证重试是否真正触发
第一步:在 handle() 开头加一行 file_put_contents('/tmp/queue.log', "run at " . date('H:i:s') . "\n", FILE_APPEND);
第二步:主动 throw new RuntimeException('simulated fail'); 强制失败
第三步:观察 /tmp/queue.log 是否出现多条时间戳,间隔是否与 retry_seconds 一致
如果只有一条记录,说明重试根本没启动——大概率是 $maxAttempts 被设为 0 或未定义,或者 Job 类没继承自 Hyperf\AsyncQueue\Job。


















