Как обработать массовую отправку почты с тайм-аутом очереди - PullRequest
0 голосов
/ 24 сентября 2019

У меня есть таблица с 100K + электронными письмами, куда я хочу ежедневно отправлять письма:

Я добавил расписание в app \ Console \ Kernel.php:

$schedule->job(new SendDailyEmails)->dailyAt('09:00');

Внутри работы у меня есть:

$users = User::all();
foreach($users as $user){
    Maill:to($user->email)->send(new DailyMail($user));
    $status = 'sent';
    if( in_array($user->email, Mail::failures()) ){
        $status = 'failed';
        Log::error($user->email . ' was not sent.');
    }else{
        Log::info($user->email . ' was sent.');
    }
    SentMail::create([
        'email' => $user->email,
        'status' => $status
    ]);

}

Это работает нормально, но через некоторое время это останавливается, вероятно, из-за тайм-аута задания.В таблице failed_jobs я получаю MaxAttemptsExceededException с сообщением о том, что Job пытался выполнить слишком много раз или выполняется слишком долго.Так как я установил очередь пытается максимум 3 в супервизоре, он должен идти только 3 раза.И, проверяя вещи, он не пытался повторить попытку, потому что я получил одно письмо вместо 3.

Так что дело доходит до тайм-аута, и я не уверен, что является значением по умолчанию, но имеет ли это значение, так как я не буду знать, каксколько времени потребуется, чтобы отправить все электронные письма?

Должен ли я разделить почту на группы по 50 и вызвать отдельные экземпляры заданий для каждой группы?

У кого-нибудь есть хороший рабочий ответ на этот вопрос?

Ответы [ 2 ]

0 голосов
/ 24 сентября 2019

Вместо того, чтобы пытаться отправлять 100k + электронных писем одновременно в классе, отправьте 100k + экземпляров Задания работнику очереди

$users = User::all();
foreach($users as $user){
  $schedule->job(new SendDailyEmails($user))->dailyAt('09:00');
}

Теперь Laravel будет складывать 100k + задание s в очереди и попытка отправить одно электронное письмо одному пользователю за раз

class SendDailyEmails implements ShouldQueue
{
  public $user;

  public function __construct(User $user)
  {
    $this->user = $user;
  }

  Maill:to($this->user->email)->send(new DailyMail($user));
    $status = 'sent';
    if( in_array($this->user->email, Mail::failures()) ){
        $status = 'failed';
        Log::error($this->user->email . ' was not sent.');
    }else{
        Log::info($this->user->email . ' was sent.');
    }
    SentMail::create([
        'email' => $this->user->email,
        'status' => $status
    ]);
0 голосов
/ 24 сентября 2019

Если вы посмотрите официальную документацию , вы обнаружите, что каждое отдельное письмо может быть поставлено в очередь.

Итак, вам следует сменить работу с

Mail:to($user->email)->send(new DailyMail($user));

до

Mail:to($user->email)->queue(new DailyMail($user));

Таким образом, вы будете помещать в очередь каждую почту, создаваемую вашей работой.Я предлагаю вам создать определенную очередь и использовать систему, такую ​​как laravel horizon для лучшего мониторинга.

Также не забывайте разбивать на части и задерживать отправку, потому что ваше приложение может запустить ошибку тайм-аута, но такжепровайдеры, такие как mailgun , могут заблокировать вашу отправку, если увидят необычную активность.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...