Laravel email with queue 550 error (too many emails per second)

泄露秘密 提交于 2019-11-28 13:30:45

Seems like only Mailtrap sends this error, so either open another account or upgrade to a paid plan.

I finally figured out how to set up the entire Laravel app to throttle mail based on a config.

In the boot() function of AppServiceProvider,

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

In config/mail.php, add this line:

'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.

In your .env files, add a line like:

MAIL_THROTTLE_TO_MESSAGES_PER_MIN=50

The only problem is that it doesn't seem to affect mail sent via the later() function if QUEUE_DRIVER=sync.

Maybe you should make sure it was really sent via Sendgrid and not mailtrap. Their hard rate limit seems currently to be 3k requests per second against 3 requests per second for mailtrap on free plan :)

For debugging only!
If you don't expect more then 5 emails and don't have the option to change mailtrap, try:

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
    }
}

Using sleep() is a really bad practice. In theory this code should only execute in test environment or in debug mode.

I achieved this on Laravel v5.8 by setting the Authentication routes manually. The routes are located on the file routes\web.php. Below are the routes that needs to be added to that file:

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');
});

Explanation:

  • Do not pass any parameter to the route Auth::routes(); to let configure the authentication routes manually.
  • Wrap the route email/resend inside a Route::group with the middleware throttle:1,1 (the two numbers represents the max retry attempts and the time in minutes for those max retries)

I also removed a line of code in the file app\Http\Controllers\Auth\VerificationController.php in the __construct function.

I removed this:

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

You need to rate limit emails queue.

The "official" way is to setup Redis queue driver. But it's hard and time consuming.

So I wrote custom queue worker mxl/laravel-queue-rate-limit that uses Illuminate\Cache\RateLimiter to rate limit job execution (the same one that used internally by Laravel to rate limit HTTP requests).

In config/queue.php specify rate limit for emails queue (for example 2 emails per second):

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

And run worker for this queue:

$ php artisan queue:work --queue emails
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!