laravel异步事件中`shouldQueue`存在的问题
测试异步队列事件shouldQueue存在的问题,方法如下:
- 在
shouldQueue添加判断条件,值小于5继续操作 - 在
handle中阻塞操作,并添加条件
代码如下:
<?php
namespace App\Listeners\Test;
use App\Events\Test\RollEvent;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Support\Facades\Redis;
class CapterBEventListener implements ShouldQueue
{
use InteractsWithQueue;
public function handle(RollEvent $event)
{
$redis = Redis::class;
$num = $redis::incr('capter_test');
echo '执行:',$num,PHP_EOL;
$redis::expire('capter_test', 3600);
sleep(10);
echo '结束',PHP_EOL;
}
public function shouldQueue(RollEvent $event)
{
$redis = Redis::class;
$num = $redis::exists('capter_test') ? (int)$redis::get('capter_test') : 0;
return $num < 5;
}
}
测试如下:
- 执行结果跑了10遍
- 预期应该是跑4遍后停止
$ php artisan tinker >>> for($i = 0; $i < 10; $i++) event(new App\Events\Test\RollEvent(5))
我想可能有人会说,那么你把判断条件放到handle中不就好了吗?例如:
public function handle(RollEvent $event)
{
$redis = Redis::class;
$num = $redis::exists('capter_test') ? (int)$redis::get('capter_test') : 0;
if ($num < 5)
{
// doit...
}
}
那么不就回到主题了么,shouldQueue方法不是很鸡肋么?还有存在的意义么?同步事件又不支持,异步事件又存在问题?
上面我用
redis是为了便于举例,同理,比如操作mysql数据库,或者发起异步请求,同样会出现类似的问题
关于 LearnKu
你大概对
shouldQueue方法有些误解,shouldQueue是决定是否将任务放入队列。在你执行循环
的时候,这10个次时间是同一时间触发的,并不会因为你在
handle()方法中添加了sleep()就会阻塞,sleep()只会在队列执行时阻塞,但因为是异步,所以这里是没意义的。事件的触发流程大概是这样:
怎么来验证这一点呢? 在你的代码中添加日志打印
你会发现 10 次日志的值全部是 0,一瞬间就被打印出来了,但此时你的队列还没执行完。
所以如果你要添加阻塞,应该在
shouldQueue或for中去添加,而不是在handle中添加阻塞,最好是在 for 中添加,这样不会影响 shouldQueue 的逻辑。但这样会造成请求阻塞,如果不想请求阻塞,就在
handle中去动态判断。结论:
在
shouldQueue方法中,不应该以队列中不断变化的值去作为判断,因为队列是异步,而shouldQueue是在队列分发之前同步执行的,速度要快于队列,很可能会造成误判。shouldQueue方法是没问题的,我们在怀疑一个事情的时候应该自己先仔细求证,不要着急下结论。