Ответ
Прямой последовательный вызов метода отправки для каждого email в цикле неэффективен и приведёт к таймаутам. Я решаю эту задачу, используя асинхронную обработку через очередь заданий (Job Queue).
1. Базовый класс для отправки одного письма (например, в Laravel):
// AppMailNewsletterMailer.php
namespace AppMail;
use AppModelsSubscriber;
class NewsletterMailer
{
public function send(string $email, string $content): bool
{
// Логика отправки одного письма через SMTP, Mailgun, SendGrid и т.д.
// Например, использование встроенного Laravel Mail
Mail::to($email)->send(new AppMailNewsletter($content));
// Логируем результат или обрабатываем ошибки
Log::info("Newsletter sent to: {$email}");
return true;
}
}
2. Создание задания для очереди:
// AppJobsSendNewsletterJob.php
namespace AppJobs;
use IlluminateBusQueueable;
use IlluminateContractsQueueShouldQueue;
use IlluminateFoundationBusDispatchable;
use IlluminateQueueInteractsWithQueue;
use IlluminateQueueSerializesModels;
use AppMailNewsletterMailer;
class SendNewsletterJob implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
public function __construct(
public string $email,
public string $content
) {}
public function handle(NewsletterMailer $mailer): void
{
// Отправляем одно письмо
$mailer->send($this->email, $this->content);
}
}
3. Постановка всех писем в очередь (например, в команде Artisan):
// AppConsoleCommandsSendNewsletter.php
public function handle()
{
$subscribers = Subscriber::where('is_active', true)->cursor(); // Используем курсор для экономии памяти
$content = $this->getNewsletterContent();
foreach ($subscribers as $subscriber) {
// Вместо немедленной отправки — кладём задание в очередь
SendNewsletterJob::dispatch($subscriber->email, $content);
// Можно использовать задержку для регулировки нагрузки
// SendNewsletterJob::dispatch($subscriber->email, $content)->delay(now()->addSeconds(5));
}
$this->info('All newsletter jobs have been queued.');
}
Преимущества такого подхода:
- Масштабируемость: Воркеры могут обрабатывать очередь параллельно.
- Отказоустойчивость: Если отправка одного письма упадёт, задание можно повторить.
- Производительность: HTTP-запрос или консольная команда завершается мгновенно, не дожидаясь отправки тысяч писем.
- Контроль нагрузки: Можно регулировать количество воркеров и скорость обработки.
Для отправки миллионов писем лучше использовать специализированные сервисы (SendGrid, Amazon SES), которые предоставляют свои API и очереди.