带有队列 550 错误的 Laravel 电子邮件(每秒电子邮件太多)

Posted

技术标签:

【中文标题】带有队列 550 错误的 Laravel 电子邮件(每秒电子邮件太多)【英文标题】:Laravel email with queue 550 error (too many emails per second) 【发布时间】:2016-05-20 03:43:33 【问题描述】:

我们的电子邮件无法使用带有 Redis 队列的 Laravel 发送。

触发错误的代码是这样的:->onQueue('emails')

$job = (new SendNewEmail($sender, $recipients))->onQueue('emails');
$job_result = $this->dispatch($job);

结合这个在工作中:

use InteractsWithQueue;

我们的错误信息是:

Feb 09 17:15:57 laravel: message repeated 7947 times: [ production.ERROR: exception 'Swift_TransportException' with message 'Expected response code 354 but got code "550", with message "550 5.7.0 Requested action not taken: too many emails per second "' in /home/laravel/app/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/AbstractSmtpTransport.php:383 Stack trace: #0 /home/laravel/app/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/AbstractSmtpTransport.php(281): 

我们的错误只发生在使用 Sendgrid 而不是 Mailtrap 时,它会欺骗电子邮件发送。我已经和 Sendgrid 谈过了,当我发生错误时,电子邮件从未接触过他们的服务器,而且他们的服务完全处于活动状态。所以,错误似乎就在我身上。

有什么想法吗?

【问题讨论】:

这是来自邮件服务器的响应码,所以如果你使用的是Sendgrid的服务器,错误必须来自他们 查看您尝试发送的电子邮件数量 (7947) 我认为您应该查看 SendGrid 的批量电子邮件选项。我们在以前的工作中遇到了同样的问题,我们最终使用 MailChimp 并使用他们的 API 来同步订阅者列表。我猜 SendGrid 可能会有相同的功能或类似的东西。 只有 2 封电子邮件。由于失败,它一直尝试重新发送 8000 次。 我正在尝试解决同样的问题。我想知道 Laravel 中是否可以启用某种速率限制,例如在 Node 中:dorelljames.com/web-development/… 这里有一些东西供我们探索:***.com/questions/30568069/… 【参考方案1】:

似乎只有Mailtrap 发送此错误,因此请打开另一个帐户或升级到付费计划。

【讨论】:

或者只是延迟你的邮件超过 10 秒【参考方案2】:

也许您应该确保它确实是通过 Sendgrid 而不是 mailtrap 发送的。他们的硬速率限制目前似乎是每秒 3k 请求,而免费计划中的 mailtrap 每秒请求 3 次 :)

【讨论】:

我可能弄错了——但 Sendgrid 不是用于发送邮件吗? Mailtrap 用于不发送邮件,而是为了测试目的而捕获它。所以这并不能解决那些尝试测试群发邮件的问题。【参考方案3】:

仅用于调试! 如果您不希望收到超过 5 封电子邮件并且无法更改 ma​​iltrap,请尝试:

foreach ($emails as $email) 
    ...
    Mail::send(... $email);                                                                      
    if(env('MAIL_HOST', false) == 'smtp.mailtrap.io')
        sleep(1); //use usleep(500000) for half a second or less
    

使用sleep() 是一种非常糟糕的做法。理论上,这段代码应该只在测试环境或调试模式下执行。

【讨论】:

【参考方案4】:

我终于想出了如何设置整个 Laravel 应用来根据配置限制邮件。

AppServiceProviderboot()函数中,

$throttleRate = config('mail.throttleToMessagesPerMin');
if ($throttleRate) 
    $throttlerPlugin = new \Swift_Plugins_ThrottlerPlugin($throttleRate, \Swift_Plugins_ThrottlerPlugin::MESSAGES_PER_MINUTE);
    Mail::getSwiftMailer()->registerPlugin($throttlerPlugin);

config/mail.php 中,添加这一行:

'throttleToMessagesPerMin' => env('MAIL_THROTTLE_TO_MESSAGES_PER_MIN', null), //https://mailtrap.io has a rate limit of 2 emails/sec per inbox, but consider being even more conservative.

在您的 .env 文件中,添加如下行:

MAIL_THROTTLE_TO_MESSAGES_PER_MIN=50

唯一的问题是,如果QUEUE_DRIVER=sync,它似乎不会影响通过later() 函数发送的邮件。

【讨论】:

如果您需要更多粒度,也可以使用\Swift_Plugins_ThrottlerPlugin::MESSAGES_PER_SECOND【参考方案5】:

我通过手动设置身份验证路由在 Laravel v5.8 上实现了这一点。路线位于文件routes\web.php。以下是需要添加到该文件的路由:

Auth::routes();

Route::get('email/verify', 'Auth\VerificationController@show')->name('verification.notice');
Route::get('email/verify/id', 'Auth\VerificationController@verify')->name('verification.verify');

Route::group(['middleware' => 'throttle:1,1'], function()
    Route::get('email/resend', 'Auth\VerificationController@resend')->name('verification.resend');
);

说明:

不要将任何参数传递给路由Auth::routes();,以便手动配置身份验证路由。 使用中间件 throttle:1,1 将路由 email/resend 包裹在 Route::group 中(这两个数字代表最大重试次数和这些最大重试次数的分钟数)

我还在__construct函数中删除了文件app\Http\Controllers\Auth\VerificationController.php中的一行代码。

我删除了这个:

$this->middleware('throttle:6,1')->only('verify', 'resend');

【讨论】:

这个答案解决了测试 laravel 5.8 验证码的问题。【参考方案6】:

你需要限制emails队列的速率。

“官方”方式是设置Redis queue driver。但这既困难又耗时。

所以我编写了自定义队列工作者 mxl/laravel-queue-rate-limit,它使用 Illuminate\Cache\RateLimiter 来限制作业执行(与 Laravel 内部使用的 rate limit HTTP requests 相同)。

config/queue.php 中指定emails 队列的速率限制(例如每秒2 封电子邮件):

'rateLimit' => [
    'emails' => [
        'allows' => 2,
        'every' => 1
    ]
]

并为此队列运行工作者:

$ php artisan queue:work --queue emails

【讨论】:

【参考方案7】:

我使用sleep(5) 等待五秒钟,然后再次使用邮件陷阱。

foreach ($this->suscriptores as $suscriptor)  
    \Mail::to($suscriptor->email)
           ->send(new BoletinMail($suscriptor, $sermones, $entradas));
    sleep(5);

我在foreach 中使用了sleep(5)。 foreach 遍历存储在数据库中的所有电子邮件,sleep(5) 将循环暂停五秒钟,然后继续处理下一封电子邮件。

【讨论】:

你把那个睡觉放在哪里了?【参考方案8】:

我在使用邮件陷阱时遇到了这个问题。我在一秒钟内发送了 10 封邮件,它被视为垃圾邮件。我不得不在每项工作之间进行延迟。看看解决方案(它的 Laravel - 我有 system_jobs 表并使用 queue=database

创建一个静态函数来检查最后一个作业时间,然后添加到它self::DELAY_IN_SECONDS - 您希望作业之间的秒数:

public static function addSecondsToQueue() 
        $job = SystemJobs::orderBy('available_at', 'desc')->first();
        if($job) 
            $now = Carbon::now()->timestamp;
            $jobTimestamp = $job->available_at + self::DELAY_IN_SECONDS;
            $result = $jobTimestamp - $now;
            return $result;
         else 
            return 0;
        

    

然后用它来延迟发送消息(考虑到队列中的最后一个作业)

Mail::to($mail)->later(SystemJobs::addSecondsToQueue(), new SendMailable($params));

【讨论】:

以上是关于带有队列 550 错误的 Laravel 电子邮件(每秒电子邮件太多)的主要内容,如果未能解决你的问题,请参考以下文章

laravel 队列守护进程邮件因 SSL 错误而停止运行

压缩文件中不允许使用550个可执行文件

预期响应代码 250,但得到代码“550”,消息“550 5.7.1 Relaying denied”

laravel 队列重启

Laravel 邮件队列未发布到邮件陷阱

Laravel 邮件队列无限循环异常