【问题标题】:How to implement a queue delay with Redis :: throttle when sending mail in Laravel?在 Laravel 中发送邮件时如何使用 Redis::throttle 实现队列延迟?
【发布时间】:2019-06-05 19:52:51
【问题描述】:

我为该网站撰写时事通讯。我想限制每分钟发送的信件(队列)。对于限制,我决定使用队列和 redis::throttle。但是当我运行 php artisan queue:work --tries=2 时,日志中的一些电子邮件丢失了......

// Console command
$mailingList = MailingList::find(1);
dispatch(new SendDailyNewsletter($mailingList));

//App\Jobs\SendDailyNewsletter.php
class SendDailyNewsletter implements ShouldQueue
{
    use InteractsWithQueue, Queueable, SerializesModels;

    //...

    public function handle()
    {
        $subscriptions = DB::table('mail as m')->select(['m.email'])->where('m.id', $this->mailing_list->id)->get();

        $subscriptions->each(function ($subscription) {
            logger($subscription->email);
        });

        $subscriptions->each(function ($subscription) {
            logger('+');
            Redis::throttle('key')->allow(1)->every(5)->then(function () use ($subscription) {
                logger($subscription->email);
            }, function () {
                return $this->release(5);
            });
        });
    }
}

输出:

// foreach here all emails good
[2019-06-05 13:24:30] local.DEBUG: korwru@example.com  
[2019-06-05 13:24:30] local.DEBUG: test@example.com  
[2019-06-05 13:24:30] local.DEBUG: jackson33@example.com  
[2019-06-05 13:24:30] local.DEBUG: hollie.emmerich@example.com  
[2019-06-05 13:24:30] local.DEBUG: nbrakus@example.com  
[2019-06-05 13:24:30] local.DEBUG: estrella.christiansen@example.com  
[2019-06-05 13:24:30] local.DEBUG: elinor.frami@example.com  

//Redis::throttle some emails missed. Why?
[2019-06-05 13:24:30] local.DEBUG: +  
[2019-06-05 13:24:30] local.DEBUG: korwru@example.com  
[2019-06-05 13:24:30] local.DEBUG: +  
[2019-06-05 13:24:33] local.DEBUG: +  
[2019-06-05 13:24:35] local.DEBUG: jackson33@example.com  
[2019-06-05 13:24:35] local.DEBUG: +  
[2019-06-05 13:24:38] local.DEBUG: +  
[2019-06-05 13:24:40] local.DEBUG: nbrakus@example.com  
[2019-06-05 13:24:40] local.DEBUG: +  
[2019-06-05 13:24:43] local.DEBUG: +

告诉我为什么脚本会跳过一些数据(电子邮件)?

【问题讨论】:

    标签: php laravel


    【解决方案1】:

    您希望每 5 秒发送一封电子邮件。这个问题的一个简单解决方案是sleep 命令:

    $subscriptions->each(function ($subscription) {
        // send email to:
        logger($subscription->email);
        sleep(5);
    });
    

    此解决方案有一些主要缺点:您有一个运行时间很长的作业阻塞了您的队列。此外,如果此操作失败,重试此作业可能会导致重新发送一些电子邮件。

    您想要的是为每个订阅者提供一份工作:

    // SendDailyNewsletter
    public function handle()
    {
        $subscriptions = DB::table()..;
    
        // for every recipient of your newsletter create a new job
        $subscriptions->each(function ($subscription) {            
            SendDailyNewsletterToSubscriber::dispatch($subscription->email);
        });
    }
    

    现在我们可以使用 Redis::throttle 每 5 秒只发送一封电子邮件:

    // handle function of SendDailyNewsletterToSubscriber
    public function handle()
    {
        Redis::throttle('key')->allow(1)->every(5)->then(function () {
            // send email to subscriber
            logger($this->email);
        }, function () {
            // could not obtain lock, retry this job in 5 seconds.
            return $this->release(5);
        });
    }
    

    让我解释一下你的脚本中发生了什么:在each() 循环的第一次尝试中,Redis 尝试获取key 上的锁定并且可以获取它。在第二次迭代(test@example.com)中,Redis 再次尝试获取锁,但 3 秒后它放弃了(正在跳过电子邮件)。在第三次迭代中,它可以在 2 秒后获得锁......

    您可以使用block() 增加锁的等待时间。但这个解决方案基本上与使用 sleep() 命令相同,但有所有缺点。

    $subscriptions->each(function ($subscription) {
        logger('+');
        Redis::throttle('key')->allow(1)->every(5)->block(5)->then(function () use ($subscription) {
            logger($subscription->email);
        }, function () {
            return $this->release(5);
        });
    });
    

    【讨论】:

      猜你喜欢
      • 2016-12-28
      • 1970-01-01
      • 2019-01-30
      • 2022-11-19
      • 2016-05-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-08-22
      相关资源
      最近更新 更多