laravel源码分析-队列Queue

队列 (Queue) 是 laravel 中比较常用的一个功能,队列的目的是将耗时的任务延时处理,比如发送邮件,从而大幅度缩短 Web 请求和响应的时间。本文我们就来分析下队列创建和执行的源码。

先通过命令创建一个 Job 类,成功之后会创建如下文件 laravel-src/laravel/app/Jobs/DemoJob.php。

> php artisan make:job DemoJob

> Job created successfully.

下面我们来分析一下 Job 类的具体生成过程。

执行 php artisan make:job DemoJob 后,会触发调用如下方法。

laravel-src/laravel/vendor/laravel/framework/src/Illuminate/Foundation/Providers/ArtisanServiceProvider.php

/**
 * Register the command.

 * [A] make:job 时触发的方法
 * @return void
 */
protected function registerJobMakeCommand()
{
    $this->app->singleton('command.job.make', function ($app) {
        return new JobMakeCommand($app['files']);
    });
}

接着我们来看下 JobMakeCommand 这个类,这个类里面没有过多的处理逻辑,处理方法在其父类中。

class JobMakeCommand extends GeneratorCommand

我们直接看父类中的处理方法,GeneratorCommand->handle(),以下是该方法中的主要方法。

public function handle()
{
    // 获取类名
    $name = $this->qualifyClass($this->getNameInput());
    // 获取文件路径
    $path = $this->getPath($name);
    // 创建目录和文件
    $this->makeDirectory($path);
    // buildClass() 通过模板获取新类文件的内容
    $this->files->put($path, $this->buildClass($name));
    // $this->type 在子类中定义好了,例如 JobMakeCommand 中 type = 'Job'
    $this->info($this->type.' created successfully.');
}

方法就是通过目录和文件,创建对应的类文件,至于新文件的内容,都是基于已经设置好的模板来创建的,具体的内容在 buildClass($name) 方法中。

protected function buildClass($name)
{
    // 得到类文件模板,getStub() 在子类中有实现,具体看 JobMakeCommand
    $stub = $this->files->get($this->getStub());
    // 用实际的name来替换模板中的内容,都是关键词替换
    return $this->replaceNamespace($stub, $name)->replaceClass($stub, $name);
}

获取模板文件

protected function getStub()
{
    return $this->option('sync')
                    ? __DIR__.'/stubs/job.stub'
                    : __DIR__.'/stubs/job-queued.stub';
}

job.stub

job-queued.stub

下面看一下前面我们创建的一个Job类,DemoJob.php,就是来源于模板 job-queued.stub。

至此,我们已经大致明白了队列任务类是如何创建的了。下面我们来分析下其是如何生效运行的。

任务类创建后,我们就可以在需要的地方进行任务的分发,常见的方法如下:

DemoJob::dispatch(); // 任务分发
DemoJob::dispatchNow(); // 同步调度,队列任务不会排队,并立即在当前进程中进行

下面先以 dispatch() 为例分析下分发过程。

trait Dispatchable
{
    public static function dispatch()
    {
        return new PendingDispatch(new static(...func_get_args()));
    }
}
class PendingDispatch
{
    protected $job;

    public function __construct($job)
    {   echo '[Max] ' . 'PendingDispatch ' . '__construct' . PHP_EOL;
        $this->job = $job;
    }

    public function __destruct()
    {   echo '[Max] ' . 'PendingDispatch ' . '__destruct' . PHP_EOL;
        app(Dispatcher::class)->dispatch($this->job);
    }
}

重点是 app(Dispatcher::class)->dispatch($this->job) 这部分。

我们先来分析下前部分 app(Dispatcher::class),它是在 laravel 框架中自带的 BusServiceProvider 中向 $app 中注入的。

class BusServiceProvider extends ServiceProvider implements DeferrableProvider
{
    public function register()
    {
        $this->app->singleton(Dispatcher::class, function ($app) {
            return new Dispatcher($app, function ($connection = null) use ($app) {
                return $app[QueueFactoryContract::class]->connection($connection);
            });
        });
    }
}

看一下 Dispatcher 的构造方法,至此,我们已经知道前半部分 app(Dispatcher::class) 是如何来的了。

class Dispatcher implements QueueingDispatcher
{
    protected $container;
    protected $pipeline;
    protected $queueResolver;

    public function __construct(Container $container, Closure $queueResolver = null)
    {
        $this->container = $container;
        /**
         * Illuminate/Bus/BusServiceProvider.php->register()中
         * $queueResolver 传入的是一个闭包
         * function ($connection = null) use ($app) {
         *   return $app[QueueFactoryContract::class]->connection($connection);
         * }
         */
        $this->queueResolver = $queueResolver;
        $this->pipeline = new Pipeline($container);
    }

    public function dispatch($command)
    {
        if ($this->queueResolver && $this->commandShouldBeQueued($command)) {
                // 将 $command 存入队列
            return $this->dispatchToQueue($command);
        }
        return $this->dispatchNow($command);
    }
}

BusServiceProvider 中注册了 Dispatcher::class ,然后 app(Dispatcher::class)->dispatch($this->job) 调用的即是 Dispatcher->dispatch()。

public function dispatchToQueue($command)
{
    // 获取任务所属的 connection
    $connection = $command->connection ?? null;
    /*
     * 获取队列实例,根据config/queue.php中的配置
     * 此处我们配置 QUEUE_CONNECTION=redis 为例,则获取的是RedisQueue
     * 至于如何通过 QUEUE_CONNECTION 的配置获取 queue ,此处先跳过,本文后面会具体分析。
     */
    $queue = call_user_func($this->queueResolver, $connection);

    if (! $queue instanceof Queue) {
        throw new RuntimeException('Queue resolver did not return a Queue implementation.');
    }
    // 我们创建的DemoJob无queue方法,则不会调用
    if (method_exists($command, 'queue')) {
        return $command->queue($queue, $command);
    }
    // 将 job 放入队列
    return $this->pushCommandToQueue($queue, $command);
}

protected function pushCommandToQueue($queue, $command)
{
    // 在指定了 queue 或者 delay 时会调用不同的方法,基本大同小异
    if (isset($command->queue, $command->delay)) {
        return $queue->laterOn($command->queue, $command->delay, $command);
    }

    if (isset($command->queue)) {
        return $queue->pushOn($command->queue, $command);
    }

    if (isset($command->delay)) {
        return $queue->later($command->delay, $command);
    }
    // 此处我们先看最简单的无参数时的情况,调用push()
    return $queue->push($command);
}

笔者的配置是 QUEUE_CONNECTION=redis ,估以此来分析,其他类型的原理基本类似。

配置的是 redis 时, $queue 是 RedisQueue 实例,下面我们看下 RedisQueue->push() 的内容。

Illuminate/Queue/RedisQueue.php

public function push($job, $data = '', $queue = null)
{
    /**
     * 获取队列名称
     * var_dump($this->getQueue($queue));
     * 创建统一的 payload,转成 json
     * var_dump($this->createPayload($job, $this->getQueue($queue), $data));
     */
    // 将任务和数据存入队列
    return $this->pushRaw($this->createPayload($job, $this->getQueue($queue), $data), $queue);
}

public function pushRaw($payload, $queue = null, array $options = [])
{
    // 写入redis中
    $this->getConnection()->eval(
        LuaScripts::push(), 2, $this->getQueue($queue),
        $this->getQueue($queue).':notify', $payload
    );
    // 返回id
    return json_decode($payload, true)['id'] ?? null;
}

至此,我们已经分析完了任务是如何被加入到队列中的。

Original: https://www.cnblogs.com/immaxfang/p/15775547.html
Author: immaxfang
Title: laravel源码分析-队列Queue

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/582240/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • 015 Linux 标准输入输出、重定向、管道和后台启动进程命令

    1 三种标准输入输出 2 什么是重定向?如何重定向? (1)什么是重定向? (2)如何重定向? 3 管道符以及和它容易混淆的一些符号使用 (1)管道符 | (2)&和&am…

    Linux 2023年5月27日
    0103
  • vscode搜索所有文件夹中所有文件的方法

    最近在看opencv相关的内容,看到画图这一部分时,提示我 这些代码都来自OpenCV代码的sample文件夹。 按照他的提示,我打开了相应的文件夹,却发现,so many 文件 …

    Linux 2023年6月14日
    0262
  • 关于如何在Idea下进行多子项目及引用内部子项目情况下打包项目的方法

    近期在开发Java的时候遇到了如下的打包上的问题 需要将一个工程下面的子工程分别打包 有的子工程还包含了另一个子工程 在这种情况下打包会出现找不到子模块的情况。 JDK:1.8 开…

    Linux 2023年6月14日
    092
  • 分析redis key大小的几种方法

    当redis被用作缓存时,有时我们希望了解key的大小分布,或者想知道哪些key占的空间比较大。本文提供了几种方法。 一. bigKeys 这是redis-cli自带的一个命令。对…

    Linux 2023年5月28日
    0116
  • [转帖]shell 学习之for语句

    404. 抱歉,您访问的资源不存在。 可能是网址有误,或者对应的内容被删除,或者处于私有状态。 代码改变世界,联系邮箱 contact@cnblogs.com 园子的商业化努力-困…

    Linux 2023年5月28日
    088
  • 关于 Promise 的一些简单理解

    一、ES6 中的 Promise 1、JS 如何解决 异步问题? (1)什么是 同步、异步?同步指的是 需要等待 前一个处理 完成,才会进行 下一个处理。异步指的是 不需要等待 前…

    Linux 2023年6月11日
    0109
  • CentOS7安装MYSQL8.X详细教程

    镜像下载、域名解析、时间同步请点击阿里云开源镜像站 1-首先查看系统是否存在mysql,无则不返回 rpm -qa|grep mysql 2-安装wget yum -y insta…

    Linux 2023年5月27日
    0113
  • Linux 下重启 PHP 服务、nginx 服务

    一、重启 PHP 服务 service php-fpm start 开启 service php-fpm stop 停止 service php-fpm restart 重启 二、…

    Linux 2023年6月13日
    078
  • Linux 用户密码不能设置问题

    404. 抱歉,您访问的资源不存在。 可能是网址有误,或者对应的内容被删除,或者处于私有状态。 代码改变世界,联系邮箱 contact@cnblogs.com 园子的商业化努力-困…

    Linux 2023年6月7日
    0120
  • 【镜像取证篇】DD系统镜像仿真问题的一些补充说明

    【镜像取证篇】DD系统镜像仿真问题的一些补充说明 ​ 系统千千万,环境占一半,遇到问题建议多重新挂载镜像,多尝试,站在岸上永远学不会游泳。—【蘇小沐】 【镜像取证篇】D…

    Linux 2023年6月13日
    0116
  • ADB和Fastboot最新版的谷歌官方下载链接

    最新ADB及Fastboot版本说明(SDK Platform Tools 版本说明) ADB和Fastboot for Windows ADB和Fastboot for Mac …

    Linux 2023年6月7日
    097
  • 上班摸鱼与网络安全

    上班不摸鱼,那这班上的没有灵魂啊。但是不久前爆出的国美网络监控事件,也提示我们网络有风险,摸鱼需谨慎。 https://baijiahao.baidu.com/s?id=17167…

    Linux 2023年6月13日
    0100
  • jmeter 函数之 _RandomString

    jmeter中有许多函数,学会函数的灵活使用,对于很多工作都是事半功倍的效果,今天先学习函数——__RandomString,该函数可以自定义字符长度。做接口自动化测试时,可以使用…

    Linux 2023年6月8日
    0111
  • 从磁盘删除Ubuntu出现的问题

    问题描述:Win10+Ubuntu双系统,利用磁盘管理工具删除了Ubuntu占用的磁盘,导致开机直接进入Grub界面,并且启动项仍有Ubuntu。 问题解决: 开机进入BIOS或启…

    Linux 2023年6月14日
    095
  • DQL

    查询语法 select 字段列表 from 表名列表 where 条件列表 group by 分组字段 having 分组后条件 order by 排序字段 limit 分页限定 …

    Linux 2023年6月7日
    070
  • MySQL — 数据查询语言

    DQL 全称 Data Query Language。数据查询语言,用来查询数据库中表的记录。 语法: select 查询列表(字段、常量、函数、表达式) from 表名; 字段别…

    Linux 2023年6月8日
    086
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球