Laravel:使用队列每小时发送1000封电子邮件

时间:2018-11-02 11:36:12

标签: laravel queue jobs

我有一个具有5000条记录的表用户(5000个用户),在我的服务器中,我每小时只能发送1000封电子邮件。 我如何使用队列每小时发送1000封电子邮件? 或如何使队列在循环内休眠?

EmailController:

class EmailController extends Controller
{
    public function sendEmail(Request $request){

         $event=(object)['content' => "Hello Laravel fans",'subject' => 'Test Email'];
         $users=User::all();
         App\Jobs\SendReminderEmail::dispatch($users,$event)
    }
}

SendReminderEmail

class SendReminderEmail implements ShouldQueue
{
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;

    public $event;

    public $email;

    public $users;

    public function __construct($users,$event)
    {
        $this->users = $users;

        $this->event = $event;
    }

    public function handle()
    {   
        foreach ($this->users as $user) {

         Mail::to($user->email)->queue(new Reminder($this->event));
        }
    }
}

2 个答案:

答案 0 :(得分:1)

Laravel具有一个非常适合您的情况的简洁功能,它称为Task Scheduling,而不是使工作休眠一小时,而是可以每小时调用一次。

为此,将作业计划添加到位于schedule()上的App\Console\Kernel方法中,如下所示:

protected function schedule(Schedule $schedule)
{
    $schedule->job(new SendReminderEmail)->hourly();
}

我还建议您使工作自成一体,这将使此任务变得更加简单,我在考虑这样的事情:

class SendReminderEmail implements ShouldQueue
{
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;

    public $event;

    public $users;

    public function __construct()
    {
        $this->users = User::all();

        $this->event = (object)['content' => "Hello Laravel fans",'subject' => 'Test Email'];;
    }

    public function handle()
    {   
        foreach ($this->users as $user) {
            Mail::to($user->email)->queue(new Reminder($this->event));
        }
    }
}

现在您可以摆脱控制器,因为此作业将每小时自动执行一次。

请记住,您将需要在服务器上运行cron以检查是否需要执行作业。如果要使用php artisan schedule:run进行测试,也可以手动运行它。

希望这对您有所帮助。

答案 1 :(得分:1)

Laravel具有内置的节流功能,用于限速作业。来自docs

  

如果您的应用程序与Redis交互,则可以限制排队的时间   时间或并发作业。此功能可以在以下情况下提供帮助   您排队的作业正在与同样受速率限制的API交互。   例如,使用节流方法,您可以节流给定类型   每60秒只能运行10次。如果不能锁   获得后,通常应将作业释放回队列,以便   以后可以重试:

Redis::throttle('key')->allow(10)->every(60)->then(function () {
    // Job logic...
}, function () {
    // Could not obtain lock...

    return $this->release(10);
});

在您的情况下,可能看起来像Redis::throttle(...)->allow(1000)->every(3600)->...

如果您不使用Redis,则另一个针对排队邮件的可能解决方案是增加延迟。同样,从docs

  

延迟的邮件队列

     

如果您希望延迟排队的发送   电子邮件,则可以使用later方法。作为第一个论点,   later方法接受一个DateTime实例,该实例指示何时   消息应该发送:

$when = now()->addMinutes(10);

Mail::to($request->user())
    ->cc($moreUsers)
    ->bcc($evenMoreUsers)
    ->later($when, new OrderShipped($order));

使用此方法将需要您计算在将电子邮件分发到队列的循环期间每封电子邮件的延迟。例如,循环的第1000次迭代可以将延迟增加1小时。

如果您使用的是Amazon SQS队列服务,则此处适用于其他排队作业的最大15分钟最大延迟也可能适用于此(我不确定这部分),在这种情况下,您将不得不提供另一种解决方案来检查您是否超出了速率限制,然后将作业释放回队列。