队列批量推送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,提问作者Андрей Двойкин
相关产品推荐
相关产品推荐

