PHP Laravel 队列技巧:Fail、Retry 或者 Delay

当创建队列jobs、监听器或订阅服务器以推送到队列中时,您可能会开始认为,一旦分派,队列工作器决定如何处理您的逻辑就完全由您自己决定了。

嗯……并不是说你不能从作业内部与队列工作器交互,但是通常情况下,哪怕你做了,也是没必要的。

这个神奇的骚操作的出现是因为“InteractsWithQueue”这个trait。.当排队作业正在从队列中拉出, 这个 [CallQueuedListener](https://github.com/laravel/framework/blob/5.8/src/Illuminate/Events/CallQueuedListener.php#L90-L104) 会检查它是否在使用 InteractsWithQueue trait, 如果是的话,框架会将底层的“队列jobs”实例注入到内部。

这个 “任务” 实例类似于一个包装了真正的 Job 类的驱动,其中包含队列连接和尝试等信息。

背景
我将以一个转码 Job 为例。 这是一个将广播音频文件转换成192kbps MP3格式的任务。因为这是在自由转码队列中设置的,所以它的作用有限。

检查尝试次数
attempts()是被调用的第一个方法, 顾名思义,它返回尝试次数,一个队列 job总是伴随着一个attempt启动。

此方法旨在与其他方法一起使用 ..., 类似 fail() 或者 release() (delay). 为了便于说明,我们将通知用户第几次重试: 每次我们尝试在空闲队列中转换(转换代码)时,我们都会通知用户我们正在第几次重试,让他可以选择取消将来的转换(转换代码)。

1 1 <?php 2 2 namespace App\Jobs; 3 3 use App\Podcast; 4 4 use Transcoder\Transcoder; 5 5 use Illuminate\Bus\Queueable; 6 6 use Illuminate\Queue\SerializesModels; 7 7 use App\Notifications\PodcastTranscoded; 8 8 use Illuminate\Queue\InteractsWithQueue; 9 9 use Illuminate\Foundation\Bus\Dispatchable; 1010 use App\Notifications\RetyingPodcastTranscode; 1111 class TranscodePodcast 1212 { 1313 use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; 1414 /** 1515 * Transcoder Instance 1616 * 1717 * @var \App\Podcast 1818 */ 1919 protected $podcast; 2020 /** 2121 * 创建一个新的转码podcast实例。 2222 * 2323 * @param \App\Podcast $podcast 2424 * @return void 2525 */ 2626 public function __construct(Podcast $podcast) 2727 { 2828 $this->podcast = $podcast; 2929 } 3030 /** 3131 * 执行队列job. 3232 * 3333 * @param \Transcoder\Transcoder $podcast 3434 * @return void 3535 */ 3636 public function handle(Transcoder $transcoder) 3737 { 3838 // 告诉用户我们第几次重试 3939 if ($this->attempts() > 1) { 4040 $this->podcast->publisher->notify(new RetryingPodcastTranscode($this->podcast, $this->attempts()); 4141 } 4242 $transcoded = $this->transcoder->setFile($event->podcast) 4343 ->format('mp3') 4444 ->bitrate(192) 4545 ->start(); 4646 4747 4848 // 将转码podcast与原始podcast关联 4949 $this->podcast->transcode()->associate($transcoded); 5050 5151 // 通知podcast的发布者他的podcast已经准备好了 5252 5353 $this->publisher->notify(new PodcastTranscoded($this->podcast)); 5454 } 5555 }

告诉用户我们将在第几次时重试某些内容,这在逻辑预先失败时很有用,让用户(或开发人员)检查出了什么问题,但当然您可以做更多的事情。

就我个人而言,我喜欢在“Job作业”失败后再这样做,如果还有重试时间,告诉他我们稍后会重试当然,这个例子只是为了举例说明。

删除作业队列 Job
第二个方法就是 delete(). 跟你猜想的一样,您可以从队列中删除当前的“队列 Job”。 当队列 Job或侦听器由于多种原因排队后不应处理时,这将会很方便,例如,考虑一下这个场景:在转码发生之前,上传podcast的发布者由于任何原因(比如TOS冲突)被停用,我们应该不处理podcast。

我们将在前面的示例中添加该代码:

1 1 <?php 2 2 namespace App\Jobs; 3 3 use App\Podcast; 4 4 use Transcoder\Transcoder; 5 5 use Illuminate\Bus\Queueable; 6 6 use Illuminate\Queue\SerializesModels; 7 7 use App\Notifications\PodcastTranscoded; 8 8 use Illuminate\Queue\InteractsWithQueue; 9 9 use Illuminate\Foundation\Bus\Dispatchable; 1010 use App\Notifications\RetyingPodcastTranscode; 1111 class TranscodePodcast 1212 { 1313 use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; 1414 /** 1515 * Transcoder Instance 1616 * 1717 * @var \App\Podcast 1818 */ 1919 protected $podcast; 2020 /** 2121 * 创建一个新的转码podcast实例。 2222 * 2323 * @param \App\Podcast $podcast 2424 * @return void 2525 */ 2626 public function __construct(Podcast $podcast) 2727 { 2828 $this->podcast = $podcast; 2929 } 3030 /** 3131 * 执行队列 job. 3232 * 3333 * @param \Transcoder\Transcoder $podcast 3434 * @return void 3535 */ 3636 public function handle(Transcoder $transcoder) 3737 { 3838 // 如果发布服务器已被停用,请删除此队列job 3939 if ($this->podcast->publisher->isDeactivated()) { 4040 $this->delete(); 4141 } 4242 // 告诉用户我们第几次重试 4343 if ($this->attempts() > 1) { 4444 $this->podcast->publisher->notify(new RetryingPodcastTranscode($this->podcast, $this->attempts()); 4545 } 4646 $transcoded = $this->transcoder->setFile($event->podcast) 4747 ->format('mp3') 4848 ->bitrate(192) 4949 ->start(); 5050 5151 // 将转码podcast与原始podcast关联 5252 $this->podcast->transcode()->associate($transcoded); 5353 5454 // 通知podcast的发布者他的podcast已经准备好了 5555 $this->publisher->notify(new PodcastTranscoded($this->podcast)); 5656 } 5757 }

如果需要删除可能已删除的模型上的作业,则可能需要 设置 [$deleteWhenMissingModels](https://laravel.com/docs/5.8/queues#ignoring-missing-models) 为真 t避免处理不存在的东西。

失败的队列job
当您需要控制人为破坏逻辑时,这非常非常方便, 因为使用空的“return”语句会将“Job”标记为已成功完成。您可以强制使排队的作业失败,希望出现异常,允许处理程序在可能的情况下稍后重试。

这使您在作业失败时可以更好地控制在任何情况下,也可以使用“failed()”方法, 它允许你 失败后执行任何清洁操纵, 比如通知用户或者删除一些东西。

在此示例中,如果由于任何原因(如 CDN 关闭时)无法从存储中检索podcast ,则作业将失败,并引发自定义异常。

1 1 <?php 2 2 namespace App\Jobs; 3 3 use App\Podcast; 4 4 use Transcoder\Transcoder; 5 5 use Illuminate\Bus\Queueable; 6 6 use Illuminate\Queue\SerializesModels; 7 7 use App\Exceptions\PodcastUnretrievable; 8 8 use App\Notifications\PodcastTranscoded; 9 9 use Illuminate\Queue\InteractsWithQueue; 1010 use Illuminate\Foundation\Bus\Dispatchable; 1111 use App\Notifications\RetyingPodcastTranscode; 1212 class TranscodePodcast 1313 { 1414 use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; 1515 /** 1616 * 转码器实例 1717 * 1818 * @var \App\Podcast 1919 */ 2020 protected $podcast; 2121 /** 2222 * 创建一个新的转码Podcast实例。 2323 * 2424 * @param \App\Podcast $podcast 2525 * @return void 2626 */ 2727 public function __construct(Podcast $podcast) 2828 { 2929 $this->podcast = $podcast; 3030 } 3131 /** 3232 * 执行队列 job. 3333 * 3434 * @param \Transcoder\Transcoder $podcast 3535 * @return void 3636 */ 3737 public function handle(Transcoder $transcoder) 3838 { 3939 // 如果发布服务器已被停用,请删除此队列job 4040 if ($this->podcast->publisher->isDeactivated()) { 4141 $this->delete(); 4242 } 4343 //如果podcast不能从storage存储中检索,我们就会失败。 4444 if ($this->podcast->fileDoesntExists()) { 4545 $this->fail(new PodcastUnretrievable($this->podcast)); 4646 } 4747 // 告诉用户我们第几次重试 4848 if ($this->attempts() > 1) { 4949 $this->podcast->publisher->notify(new RetryingPodcastTranscode($this->podcast, $this->attempts()); 5050 } 5151 5252 $transcoded = $this->transcoder->setFile($event->podcast) 5353 ->format('mp3') 5454 ->bitrate(192) 5555 ->start(); 5656 5757 // 将转码podcast与原始podcast关联 5858 $this->podcast->transcode()->associate($transcoded); 5959 6060 // 通知podcast的发布者他的podcast已经准备好了 6161 $this->publisher->notify(new PodcastTranscoded($this->podcast)); 6262 } 6363 }

现在,进入最后的方法。

释放(延迟)队列job
这可能是trait性状的最有用的方法, 因为它可以让你在未来进一步推动这项队列job. 此方法用于 队列job速率限制.

除了速率限制之外,您还可以在某些不可用但希望在不久的将来使用它的情况下使用它同时,避免先发制人地失败。

在最后一个示例中,我们将延迟转码以备稍后使用:如果转码器正在大量使用,我们将延迟转码5分钟直到负载降低。

1 1 <?php 2 2 namespace App\Jobs; 3 3 use App\Podcast; 4 4 use Transcoder\Transcoder; 5 5 use Illuminate\Bus\Queueable; 6 6 use Illuminate\Queue\SerializesModels; 7 7 use App\Exceptions\PodcastUnretrievable; 8 8 use App\Notifications\PodcastTranscoded; 9 9 use Illuminate\Queue\InteractsWithQueue; 1010 use App\Notifications\TranscoderHighUsage; 1111 use Illuminate\Foundation\Bus\Dispatchable; 1212 use App\Notifications\RetyingPodcastTranscode; 1313 class TranscodePodcast 1414 { 1515 use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; 1616 /** 1717 * Transcoder Instance 1818 * 1919 * @var \App\Podcast 2020 */ 2121 protected $podcast; 2222 /** 2323 * 创建一个新的转码podcast实例。 2424 * 2525 * @param \App\Podcast $podcast 2626 * @return void 2727 */ 2828 public function __construct(Podcast $podcast) 2929 { 3030 $this->podcast = $podcast; 3131 } 3232 /** 3333 * 执行队列job. 3434 * 3535 * @param \Transcoder\Transcoder $podcast 3636 * @return void 3737 */ 3838 public function handle(Transcoder $transcoder) 3939 { 4040 // 如果发布服务器已被停用,请删除此队列job 4141 if ($this->podcast->publisher->isDeactivated()) { 4242 $this->delete(); 4343 } 4444 // 如果podcast不能从storage存储中检索,我们就会失败。 4545 if ($this->podcast->fileDoesntExists()) { 4646 $this->fail(new PodcastUnretrievable($this->podcast)); 4747 } 4848 4949 // 如果转码器使用率很高,我们将 5050 // t延迟转码5分钟. 否则我们可能会有拖延转码器进程的危险 5151 // 它会把所有的转码子进程都记录下来。 5252 if ($transcoder->getLoad()->isHigh()) { 5353 $delay = 60 * 5; 5454 $this->podcast->publisher->notify(new TranscoderHighUsage($this->podcast, $delay)); 5555 $this->release($delay); 5656 } 5757 // 告诉用户我们第几次重试 5858 if ($this->attempts() > 1) { 5959 $this->podcast->publisher->notify(new RetryingPodcastTranscode($this->podcast, $this->attempts()); 6060 } 6161 6262 $transcoded = $this->transcoder->setFile($event->podcast) 6363 ->format('mp3') 6464 ->bitrate(192) 6565 ->start(); 6666 6767 // 将转码podcast与原始podcast关联 6868 $this->podcast->transcode()->associate($transcoded); 6969 7070 // 通知podcast的发布者他的podcast已经准备好了 7171 $this->publisher->notify(new PodcastTranscoded($this->podcast)); 7272 } 7373 }

我们可以使用一些特殊方法,例如,获得分配给转码器的一些时隙,如果转码器时隙已满,则延迟作业。 在排队的工作中,你能做的就只有这些了。排队愉快。

点赞
收藏

评论区

加载中...

相关推荐

MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1

文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s

Oracle 分组与拼接字符串同时使用

SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(

MySQL部分从库上面因为大量的临时表tmp_table造成慢查询

背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_

皕杰报表之UUID

​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为

手写Java HashMap源码

HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22

2020年前端实用代码段,为你的工作保驾护航

有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )