队列任务重试次数在任务类中通过 $tries 属性配置,而非全局配置;retry_after 仅控制单次执行超时,tries 才决定最大重试次数,且 database 驱动不支持 tries。

队列任务重试次数在哪配?retry_after 和 tries 别搞混
ThinkPHP 的队列重试逻辑分两层:底层驱动(如 Redis、Database)控制单次任务最长执行时间,用 retry_after;而任务类自身决定最多尝试几次,用 tries。很多人改了配置文件却没生效,就是因为只动了前者,没在任务类里设 tries。
-
retry_after在config/queue.php里配,比如'retry_after' => 60,表示任务运行超 60 秒就判为失败并进入重试流程 -
tries是任务类的属性,必须写在具体任务里:class SendEmailJob implements ShouldQueue { public $tries = 3; } - 如果任务类没定义
$tries,默认值是0(即无限重试),不是 1 或 3 —— 这点容易被忽略
Redis 驱动下重试失败后进 failed_jobs 表?先检查 failed 配置
用 Redis 做队列时,failed_jobs 表不会自动写入,除非你显式启用了失败日志记录。否则任务重试耗尽后直接丢弃,连错误都看不到。
- 确保
config/queue.php中'failed'配置项已启用:'failed' => [ 'driver' => 'database', 'database' => env('DB_CONNECTION', 'mysql'), 'table' => 'failed_jobs', ], - 数据库驱动本身不支持重试计数,所以必须搭配 Redis 或 Beanstalkd 等支持原子操作的驱动才能用
$tries - 如果用的是
database驱动,$tries无效,重试靠手动调用release()实现,且容易因并发导致重复执行
attempts() 方法能取到当前第几次重试,但别在 __construct 里用
任务类里想根据重试次数做不同处理(比如第一次发邮件,第三次发钉钉告警),得用 $this->attempts()。但它不是构造函数里就能拿到的 —— 因为对象实例化时重试上下文还没注入。
- 只能在
handle()方法里调用:public function handle() { if ($this->attempts() >= 2) { // 触发备用通知逻辑 } } -
attempts()返回的是当前执行轮次(从 1 开始),不是剩余次数,别和$tries - $this->attempts()混淆 - 若任务被延迟投递(
dispatch()->delay()),attempts()仍从 1 计起,延迟不计入重试次数
重试间隔默认是 0 秒,高频失败会打爆 Redis 或 DB
ThinkPHP 默认不设重试退避策略,失败后立刻重试。对下游接口或数据库压力大时,可能瞬间涌出几百个相同任务。
- 用
retryUntil()控制截止时间,比固定次数更稳妥:dispatch(new SendEmailJob())->retryUntil(now()->addMinutes(30));
- 需要指数退避?得自己实现:在
failed()方法里调用$this->release($delay),比如$this->release(pow(2, $this->attempts()) * 60) - 注意
release()只对 Redis/Beanstalkd 有效;Database 驱动不支持延迟重试,调用后无效果
重试逻辑真正生效的前提是:驱动支持、任务类定义了 $tries、失败监听器已注册、且没有在中间件或事件里意外吞掉异常。漏掉任意一环,都会让重试形同虚设。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!










