laravel - 队列(三)

1、为什么使用队列?
异步、重试

2、什么情况使用队列?
(1)、耗时比较久的,比如上传一个文件后进行一些格式的转化等。
(2)、需要保证送达率的,比如发送短信,因为要调用别人的 api,总会有几率失败,那么为了保证送达,重试就必不可少了。
使用队列的时候一定要想明白一个问题,这个任务到底是不是可以异步,如果因为异步会导致问题,那么就要放弃使用队列。

3、什么时候使用 queue:listen 什么时候使用 queue:work?
Laravel 5.3 的文档已经不写 queue:listen 这个指令怎么用了,所以你可以看出来可能官方已经不怎么建议使用 queue:listen 了,但是在本地调试的时候要使用 queue:listen,因为 queue:work 在启动后,代码修改,queue:work 不会再 Load 上下文,但是 queue:listen 仍然会重新 Load 新代码。
其余情况全部使用 queue:work 吧,因为效率更高。

4、命令讲解

php artisan queue:work --daemon --quiet --queue=default --delay=3 --sleep=3 --tries=3
--daemon

总体来说,在 supervisor 中一般要加这个 option,可以节省 CPU 使用。

--quiet

不输出任何内容

--delay=3

一个任务失败后,延迟多长时间后再重试,单位是秒。这个值的设定我个人建议不要太短,因为一个任务失败(比如网络原因),重试时间太短可能会出现连续失败的情况。

--sleep=3

去 Redis 中拿任务的时候,发现没有任务,休息多长时间,单位是秒。这个值的设定要看你的任务是否紧急,如果是那种非常紧急的任务,不能等待太长时间。

--tries=3

定义失败任务最多重试次数。这个值的设定根据任务的重要程度来确定,一般 3 次比较适合。

5、延迟分发
如果你想延迟你的队列任务的执行,你可以在分发任务的时候使用 delay 方法。例如,让我们详细说明一个十分钟之后才会执行的任务:

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use \App\Jobs\GoodsInfoToLog;
use phpDocumentor\Reflection\Types\Array_;

class JobProcessController extends Controller
{
    /**
     * Display a listing of the resource.
     *
     * @return \Illuminate\Http\Response
     */
    public function index()
    {
        //
    }

    /**
     * Show the form for creating a new resource.
     *
     * @return \Illuminate\Http\Response
     */
    public function create()
    {
        $chineseName = array('枯', '藤', '老', '树', '昏', '鸦', '小', '桥', '流', '水', '人', '家', '古', '道', '西', '风', '瘦', '马');
        $name = $chineseName[rand(0, 17)] . $chineseName[rand(0, 17)] . $chineseName[rand(0, 17)];

        $data = array(
            'price' => rand(1, 1000),
            'title' => $name,
        );

        GoodsInfoToLog::dispatch($data)->delay(now()->addSeconds(10));
        GoodsInfoToLog::dispatch($data)->delay(now()->addMinutes(1));
    }
}

6、分发任务到指定队列

<?php
namespace App\Http\Controllers;

use Illuminate\Http\Request;
use \App\Jobs\GoodsInfoToLog;
use phpDocumentor\Reflection\Types\Array_;

class JobProcessController extends Controller
{
    /**
     * Display a listing of the resource.
     *
     * @return \Illuminate\Http\Response
     */
    public function index()
    {
        //
    }

    /**
     * Show the form for creating a new resource.
     *
     * @return \Illuminate\Http\Response
     */
    public function create()
    {
        $chineseName = array('枯', '藤', '老', '树', '昏', '鸦', '小', '桥', '流', '水', '人', '家', '古', '道', '西', '风', '瘦', '马');
        $name = $chineseName[rand(0, 17)] . $chineseName[rand(0, 17)] . $chineseName[rand(0, 17)];

        $data = array(
            'price' => rand(1, 1000),
            'title' => $name,
        );

        GoodsInfoToLog::dispatch($data)->delay(now()->addSeconds(50));  //延时10秒
        GoodsInfoToLog::dispatch($data)->delay(now()->addMinutes(2));   //延时1分钟

        GoodsInfoToLog::dispatch($data)->onQueue('test')->delay(now()->addSeconds(50));  //延时10秒
        GoodsInfoToLog::dispatch($data)->onQueue('test')->delay(now()->addMinutes(2));   //延时1分钟
    }
}

普通推送进去只是一个普通的list数据结构类型,如果是延时或者失败队列是一个zset有序集合


20d7a714d5b285ea9c3fe99866d1336.png

7、最大尝试次数
在一个任务重指定最大尝试次数可以通过 Artisan 命令的 --tries 选项 指定:

php artisan queue:work --tries=3

你可能想通过任务类自身对最大任务尝试次数进行一个更颗粒化的处理。如果最大尝试次数是在任务类中定义的,它将优先于命令行中的值提供:

 /**
     * 任务可以尝试的最大次数。
     *
     * @var int
     */
    public $tries = 5;

8、基于时间尝试
作为另外一个选择来定义任务在失败前会尝试多少次,你可以定义一个任务超时时间。这样的话,在给定的时间范围内,任务可以无限次尝试。要定义一个任务的超时时间,在你的任务类中新增一个 retryUntil 方法:

<?php

namespace App\Jobs;

use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use function PHPSTORM_META\elementType;

class GoodsInfoToLog implements ShouldQueue
{
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;

    protected $goods;


    /**
     * 任务可以尝试的最大次数。
     *
     * @var int
     */
    public $tries = 100;


    /**
     * Create a new job instance.
     *
     * @return void
     */
    public function __construct($goods)
    {
        $this->goods = $goods;
    }

    /**
     * Execute the job.
     *
     * @return void
     */
    public function handle()
    {
        $goods = $this->goods;
        $goods['num'] = $this->attempts();

        Log::channel('myapplog')->info($goods);

        if ($this->attempts() < 26) {
            DB::table('test')->insert(array('title' => $goods['title'], 'price' => $goods['price'], 'create_at' => date("Y-m-d H:i:s"), 'push_at' => date("Y-m-d H:i:s")));

        } else {
            DB::table('queue_test')->insert(array('title' => $goods['title'], 'price' => $goods['price'], 'create_at' => date("Y-m-d H:i:s"), 'push_at' => date("Y-m-d H:i:s")));
        }
    }

    /**
     * 定义任务超时时间
     *
     * @return \DateTime
     */
    public function retryUntil()
    {
        return now()->addSeconds(30);
    }


//    /**
//     * 任务失败的处理过程
//     *
//     * @param  Exception  $exception
//     * @return void
//     */
//    public function failed(Exception $exception)
//    {
//        // 给用户发送任务失败的通知,等等……
//    }
}

在这里轮询限定是100次,但是执行重试30s超时就算是失败了


af7df1c3262ea889eca8c54c6931042.png

9、任务过期
在你的 config/queue.php 配置文件中,每个队列连接都定义了一个 retry_after 选项。这个选项指定了队列连接在重试一个任务前应该等它执行多久。例如,如果 retry_after 的值设置为 90 ,那么任务在执行了 90 秒后将会被放回队列而不是删除它。一般情况下,你应该将 retry_after 的值设置为你认为你的任务可能会执行需要最长时间的值。

10、任务超时

queue:work Artisan 命令包含一个 --timeout 选项。 --timeout 选项指定了 Laravel 的队列主进程在中止一个执行任务的子进程之前需要等到多久。有时一个子进程可能会因为各种原因「冻结」,比如一个外部的 HTTP 请求失去响应。 --timeout 选项会移除那些超过指定时间被冻结的进程。

php artisan queue:work --timeout=60

<?php

namespace App\Jobs;

class ProcessPodcast implements ShouldQueue
{
/**
* 任务可以执行的最大秒数 (超时时间)。
*
* @var int
*/
public $timeout = 120;
}

retry_after 配置项和 --timeout 命令行配置并不同,但将它们同时使用可以确保任务不会丢失并且任务只会成功执行一次。

注意:--timeout 的值应该比你在 retry_after 中配置的值至少短几秒。这会确保处理器永远会在一个任务被重试之前中止。如果你的 --timeout 值比 retry_after 的值长的话,你的任务可能会被执行两次。

11、处理失败的任务
         Laravel 包含了一种方便的方法来指定任务应该尝试的最大次数。如果一个任务已经到达了最大尝试次数,它就会被插入到 failed_jobs 数据库表中。
        可以直接在任务类中定义 failed 方法,允许你在任务失败时执行针对于该任务的清理工作。 这是向用户发送警报或恢复任务执行的任何操作的绝佳位置。导致任务失败的 Exception 将被传递给 failed 方法:

<?php

namespace App\Jobs;

use Exception;
use App\Podcast;
use App\AudioProcessor;
use Illuminate\Bus\Queueable;
use Illuminate\Queue\SerializesModels;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Contracts\Queue\ShouldQueue;

class ProcessPodcast implements ShouldQueue
{
use InteractsWithQueue, Queueable, SerializesModels;

protected $podcast;

/**
 * 创建任务实例
 *
 * @param  Podcast  $podcast
 * @return void
 */
public function __construct(Podcast $podcast)
{
    $this->podcast = $podcast;
}

/**
 * 执行任务
 *
 * @param  AudioProcessor  $processor
 * @return void
 */
public function handle(AudioProcessor $processor)
{
    // 上传播客……
}

/**
 * 任务失败的处理过程
 *
 * @param  Exception  $exception
 * @return void
 */
public function failed(Exception $exception)
{
    // 给用户发送任务失败的通知,等等……
}

}


重试失败的任务,要想查看所有被放入 failed_jobs 数据表中的任务,你可以使用 Artisan 命令 queue:failed :

php artisan queue:failed

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 226,979评论 6 528
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 97,750评论 3 412
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 174,647评论 0 373
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 62,299评论 1 307
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 71,104评论 6 405
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 54,623评论 1 320
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 42,716评论 3 434
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 41,873评论 0 285
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 48,380评论 1 330
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 40,344评论 3 354
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 42,496评论 1 365
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 38,055评论 5 355
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 43,756评论 3 343
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 34,146评论 0 25
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 35,410评论 1 281
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 51,072评论 3 386
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 47,485评论 2 370