You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

队列批量推送FCM通知遇cURL错误6,如何处理并继续执行?

批量推送FCM通知时遇连接错误导致队列中断的解决方法

问题场景

向大量用户推送FCM通知过程中,发送到一半触发以下连接错误,队列直接停止运行,无法继续执行剩余推送:

GuzzleHttp\Exception\ConnectException: cURL error 6: Could not resolve host: fcm.googleapis.com,报错路径:/var/www/jai/vendor/guzzlehttp/guzzle/src/Handler/CurlFactory.php:210

现有处理代码

public function handle(): void
{
    try {
        $usersGroup = User::query()->whereNotNull('fcm_token')
            ->select(['id', 'fcm_token', 'gender']);

        if ($this->group === UserNotification::GROUP_MALE) {
            $usersGroup = $usersGroup->where('gender', '=', User::MALE);
        }


        if ($this->group === UserNotification::GROUP_FEMALE) {
            $usersGroup = $usersGroup->where('gender', User::FEMALE);
        }

        $usersGroup->chunk(1500, function ($users) {

            foreach ($users as $user) {
                Log::channel('notification')
                    ->debug("Sending push notification to {$this->group} user group:", [
                        'user_id' => $user->id,
                        'fcm_token' => $user->fcm_token
                    ]);
                $this->fcmService->send($user->fcm_token, $this->title, $this->body);
            }
        });
    }catch (ConnectException $e){
        Log::error('Ошибка ConnectException: ' . $e->getMessage());
    }

}

解决方法

当前代码的核心问题是全局try-catch仅包裹了整个查询与chunk逻辑,一旦chunk内部的推送触发连接异常,会直接终止整个循环,导致队列停摆。要实现队列继续执行,需缩小异常捕获范围,并补充重试机制:

1. 捕获单用户推送异常

将try-catch移至foreach循环内部,确保单个用户推送失败不会影响其他用户的推送流程:

$usersGroup->chunk(1500, function ($users) {
    foreach ($users as $user) {
        try {
            Log::channel('notification')
                ->debug("向{$this->group}用户组推送通知:", [
                    'user_id' => $user->id,
                    'fcm_token' => $user->fcm_token
                ]);
            $this->fcmService->send($user->fcm_token, $this->title, $this->body);
        } catch (ConnectException $e) {
            Log::error("用户{$user->id}推送失败,连接错误: " . $e->getMessage());
            // 可选:标记该用户推送状态为失败,后续可单独重试
        } catch (\Exception $e) {
            // 兜底捕获所有异常,避免队列因未知错误中断
            Log::error("用户{$user->id}推送失败,未知错误: " . $e->getMessage());
        }
    }
});

2. 为连接错误增加重试逻辑

针对域名解析失败这类临时网络问题,增加重试次数可提升推送成功率:

// 在单用户推送逻辑中加入重试
$maxRetries = 3;
$retryCount = 0;
do {
    try {
        $this->fcmService->send($user->fcm_token, $this->title, $this->body);
        break;
    } catch (ConnectException $e) {
        $retryCount++;
        Log::warning("用户{$user->id}推送重试({$retryCount}/{$maxRetries}): " . $e->getMessage());
        if ($retryCount >= $maxRetries) {
            Log::error("用户{$user->id}推送最终失败: " . $e->getMessage());
            // 若无需队列重试该任务,此处可不抛出异常,继续执行后续推送
        }
        sleep(1); // 重试前短暂休眠,避免频繁请求触发限制
    }
} while ($retryCount < $maxRetries);

3. 防止批次异常终止队列

给整个chunk回调添加try-catch,避免批次内的未捕获异常终止整个队列进程:

$usersGroup->chunk(1500, function ($users) {
    try {
        foreach ($users as $user) {
            // 单用户推送逻辑(包含上述异常捕获与重试)
        }
    } catch (\Exception $e) {
        Log::error("当前批次推送失败: " . $e->getMessage());
        // 不抛出异常,队列会自动处理下一个chunk
    }
});

4. 配置队列任务全局重试(Laravel场景)

若希望整个任务在遇到严重错误时自动重试,可在任务类中定义重试规则:

public $tries = 5; // 任务最多重试5次
public $backoff = [1, 3, 5]; // 重试间隔依次为1、3、5秒

内容的提问来源于stack exchange,提问作者Андрей Двойкин

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.09 10:25:18