【问题标题】:Laravel email with queue 550 error (too many emails per second)带有队列 550 错误的 Laravel 电子邮件(每秒电子邮件太多)
【发布时间】: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/…
  • 这里有一些东西供我们探索:stackoverflow.com/questions/30568069/…

标签: php laravel smtp swiftmailer laravel-forge


【解决方案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));
      

      【讨论】:

        猜你喜欢
        • 2022-08-13
        • 2016-02-01
        • 1970-01-01
        • 2017-01-07
        • 2014-12-28
        • 2019-03-09
        • 1970-01-01
        • 1970-01-01
        • 2015-02-12
        相关资源
        最近更新 更多