大量队列任务总是 MaxAttemptsExceededException,怎么办?

一、异常信息:

在做拉取公众号粉丝数据的时候,我将任务都放入队列,一顿操作后遇到

Illuminate\Queue\MaxAttemptsExceededException: App\Jobs\SyncOfficialAccountUserJob has been attempted too many times or run too long. The job may have previously timed out. in /vendor/laravel/framework/src/Illuminate/Queue/Worker.php:599

二、程序思路

以一个15万粉丝的公众号举例,我的做法如下:
通过开放平台授权推送消息,触发事件

App\Listeners\SyncOfficialAccountUser

通过接口 user->list 取得openId 列表,一次拉取调用最多拉取10000个,一共要拉取15次;
然后每10000个按每500进行分块之后扔入队列等待执行,相当于每10000有20个任务,15万有300个任务;

$chunks = collect($data['data']['openid'])->chunk('500');
    $chunks->each(function  ($item,  $key)  {
    SyncOfficialAccountUserJob::dispatch(['appId'  =>  $this->authorizerAppid,  'list'  =>  $item->toArray()]);
});

三、相关配置如下:

.env

QUEUE_CONNECTION=redis

config/horizon.php

'waits'  =>  [
    'redis:default' => 60,
],
'trim' => [
    'recent' => 120,
    'failed' => 10080,
    'monitored' => 10080,
],
'fast_termination' => false,
'memory_limit' => 64,
'production' => [
    'supervisor-1' => [
        'connection' => 'redis',
        'queue' => ['default'],
        'balance' => 'simple',
        'processes' => 15,
        'tries' => 3,
    ],
],

四、执行结果

 失败任务 258个,成功拉取85794个粉丝。
 第一条完成时间:15:04:16,最后一条完成时间:16:26:07

五、换思路

在收到推送触发事件后将信息写入缓存,然后用定时任务去缓存中取数据,每次少量任务放入队列处理。
不知道这个可行不,有经验的朋友请留下你的脚印~~
谢谢了。

本作品采用《CC 协议》,转载必须注明作者和本文链接
《L01 基础入门》
我们将带你从零开发一个项目并部署到线上,本课程教授 Web 开发中专业、实用的技能,如 Git 工作流、Laravel Mix 前端工作流等。
《L04 微信小程序从零到发布》
从小程序个人账户申请开始,带你一步步进行开发一个微信小程序,直到提交微信控制台上线发布。
讨论数量: 5

emmmm。我现在大概实现方式也是类似于你这种,不过我把超时时间延长了。还有需要注意是需要指定队列。不然你一下子多了几百个队列,会阻塞其他任务的执行

4年前 评论

试下 timeout 选项,设置任务的最大执行时间。我的 laravel 版本是 5.7,horizon 1.4.3

config/horizon.php

return [
    'environments' => [
        'production' => [
            'supervisor-1' => [
                // ..............
                'processes'  => 3,
                'tries'      => 1,
                'timeout'    => 600, // 试下这个选项
            ],
        ],
]

或者在启动队列时加上 timeout 参数:

art horizon:work --timeout=600

vendor/laravel/horizon/src/Console/SupervisorCommand.php:17 文件中可以看到对 timeout 的解释为:

{--timeout=60 : The number of seconds a child process can run}

4年前 评论
waney (楼主) 4年前

@will_lin 指定队列
@varro timeout 选项
两个答案都用上了,最终还是要改方案,采用一次少数,做完一次再开启一个,以保证数据完整可靠。

4年前 评论
will_lin 4年前
waney (作者) (楼主) 4年前

是否可以使用队列超出现在就分出一条新的队列来处理呢如下

存储队列名称的队列
    --队列A
    --队列B
    --队列C

队列A(10000个)
   --数据--数据---数据---
队列B(10000个)
  ---数据--数据---数据---
队列C(10000个)
  ---数据--数据---数据---
4年前 评论
waney (楼主) 4年前

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!