请教关于异步事件任务的唯一的问题
在文档中,队列那章里面,Unique Jobs曾提到:文档链接
- 如果任务的另一个实例已经在队列中并且尚未完成处理,则不会分派该任务。
- 在现有任务完成处理之前,任何具有相同 product ID 的任务都将被忽略。
说明一下,我的事件驱动是基于redis,缓存驱动支持原子锁,我用一个简单的例子来提问:
事件示例:
EventServiceProvider添加事件'App\Events\Test\RollEvent' => [ 'App\Listeners\Test\CapterBEventListener', ],RollEvent默认配置,不做说明CapterBEventListener为异步侦听器,为了测试uniqueId统一设置为5,并在执行过程中阻塞10秒钟<?php namespace App\Listeners\Test; use App\Events\Test\RollEvent; use Illuminate\Contracts\Queue\ShouldBeUnique; use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Queue\InteractsWithQueue; class CapterBEventListener implements ShouldQueue, ShouldBeUnique { use InteractsWithQueue; private $_data = 0; /** * Create the event listener. * * @return void */ public function __construct() { // } /** * Handle the event. * * @param RollEvent $event * @return void */ public function handle(RollEvent $event) { echo 'RoolEventB===',PHP_EOL; sleep(10); echo '阻塞结束====',PHP_EOL; } public function shouldQueue(RollEvent $event) { return true; } public function uniqid(RollEvent $event) { return 5; } }
测试事件:
$ php artisan tinker
>>> $evet1 = new App\Events\Test\RollEvent(5)
>>> $evet2 = new App\Events\Test\RollEvent(5)
>>> event($evet1)
>>> event($evet2)
测试结果:
还是按照顺序执行了两遍
我的理解是在事件唯一ID中,第一个事件没有执行前,添加相同的ID事件,将不会执行
> [2022-04-30 18:29:17][VlGCdjmgniuAKbFuJ6QL7t7jLuhxzEN6] Processing: App\Listeners\Test\CapterBEventListener > RoolEventB=== > 阻塞结束 > [2022-04-30 18:29:27][VlGCdjmgniuAKbFuJ6QL7t7jLuhxzEN6] Processed: App\Listeners\Test\CapterBEventListener > [2022-04-30 18:29:27][uh4p7EuDmVSVEf17SrbcoUdKNQ1xVKzo] Processing: App\Listeners\Test\CapterBEventListener > RoolEventB=== > 阻塞结束 > [2022-04-30 18:29:37][uh4p7EuDmVSVEf17SrbcoUdKNQ1xVKzo] Processed: App\Listeners\Test\CapterBEventListener请问事件ID指的是什么唯一?为何相同编号却执行了两遍?
我将事件侦听器改为任务,测试成功了
<?php namespace App\Jobs; use Illuminate\Bus\Queueable; use Illuminate\Contracts\Queue\ShouldBeUnique; use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Foundation\Bus\Dispatchable; use Illuminate\Queue\InteractsWithQueue; use Illuminate\Queue\SerializesModels; class TestPodcast implements ShouldQueue, ShouldBeUnique { use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; /** * Create a new job instance. * * @return void */ public function __construct() { // } /** * Execute the job. * * @return void */ public function handle() { echo 'handle start', PHP_EOL; sleep(10); echo 'handle end', PHP_EOL; } public function uniqueId() { return 5; } }连续执行,只commit一次
>>> App\Jobs\TestPodcast::dispatch(); >>> App\Jobs\TestPodcast::dispatch();请问为什么在事件侦听器上不行呢?
关于 LearnKu
难道文档不是说job,虽然你都继承ShouldBeUnique,但是框架应该没有调用他的代码。 重复任务检查这个框架也是设计的怪怪的,我都生成唯一id了,为什么还需要投递进入队列由队列系统过滤?自己过滤难道性能不是更好吗?
很高兴你能提这个问题,我看了下源码,确实,用
event触发的任务,是没法「享受」Unique Jobs 这一特权的。event也就是用事件的形式,即使你implements ShouldQueue, ShouldBeUnique也没用,因为在投递任务到队列服务时,根本就没有检查这个特性。而用
job::distatch的方式就不一样了,参见:不过你可以试试
dispatch($event)说不定就 OK 了。抱歉同学,按照 @godruoyi 老师的说法,确实 dispatch 和 event 区别很大。
dispatch()中 shouldQueue 和 shouldUnique 方法在并发情况下都不会发生超发队列的情况,但是event()就不行,前几天我解答的另一个帖子写错了。但问题好像并没有那么简单,我又测试了
$this->dispatch(),也是会发生超发的情况。还有就是像你上面所写:
在并发下也不会发生超发的问题。
到此位置先总结一下:
目前已知 helper 中的
event()方法,控制器中的$this->dispatch()会出现 shouldQueue 和 shouldUnique 判断失效的问题,导致队列超发。而 Job 本身的静态方法
Job::dispatch()和 helper 中的dispatch()不会出现队列超发的问题。因为
Job::dispatch()和dispatch()都是使用的Dispatchable这个 trait 中的 dispatch() 方法,这个方法调用了在这个文件中,找到
shouldDispatch()方法,就能看见 @godruoyi 老师上面所说的对shouldUniqueId这个 trait 的判断。我带着疑问去翻看了一眼 PR ,发现在去年已经有人提过类似的问题,不过官方到现在还没有解决。
ShouldBeUnique doesn’t work with event listeners
但控制器中的
$this->dispatch(),它和event()方法一样,都是直接分发了队列,没有走上面的那个 trait 检查。然后我发现,这个 dispatch 其实分两种触发方式,一种是
Dispatchable这个 trait,但凡是通过这个 trait 分发出去的队列,是不会超发的,而通过Dispatcher这个类分发出去的队列,shouldQueue 和 shouldUnique 就会失效。那么现在我有一个问题,是哪里的代码影响了 shouldQueue 的判断?
我目前找到的相关代码只有这部分
今天先这样,太晚了,明天再来继续研究。
学习了