我负责该网站的新闻通讯。我想限制每分钟发送的信件(队列)。为了达到极限,我决定使用队列和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: +
告诉我为什么脚本跳过一些数据(电子邮件)?
答案 0 :(得分:2)
您想每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);
});
});