如何写一个任务队列

概述

任务队列通常被我们用来处理一些异步的耗时任务,在laravel中具体的用法可以看文档,那么任务队列是如何工作的呢?简而言之,它也是一个生产者消费者模型,队列处理器充当消费者不断消费任务,任务生产者不断向队列中塞任务。基于这种思想,我们也可以自己尝试着写一个。

消费者

既然如此,我们首先需要一个处理(消费)任务的消费者。核心代码如下:

    public function run()
    { 
        while(true){
            $taskList = $this->getTaskList();
            if(empty($taskList)){
                $this->stop();
            }
            $this->consume($taskList);
        }
    }

    protected function consume($taskList)
    {
        while(!empty($taskList)){
            $job = array_pop($taskList);
            $job = unserialize($job);
            $job->handle();
        }
    }

    protected function stop()
    {
        posix_kill($this->pid,SIGSTOP);
    }

消费者说明
利用一个死循环,在任务队列不为空的时候不断消耗队列,为空的时候则停止任务处理器进程,防止CPU空转浪费资源。在laravel的队列代码中使用的是sleep来间歇性停止进程,而不是采用上述代码中的这种SIGSTOP方式。

生产者

接下来我们就继续写一个生产者,例子中存储任务使用的是redis,核心代码如下:


    public function pushTask()
    {
        //store job in the redis or other database
        $this->storeTask();
        $this->wakeupWorker();
    }

    protected function storeTask()
    {
        //store job in the redis or other database like this
        $redis = $this->getTaskContainer();
        $redis->lpush("task_list",serialize($this->job));
        //job must implements interface hanler
    }

    protected function wakeupWorker()
    {
        posix_kill($this->workerPid,SIGCONT);
    }

生产者说明
当有任务到来的时候,把任务持久化到某个任务容器中,然后唤醒消费者进程,使其消费任务。

结语

具体的代码示例,见 代码示例,测试用例只能运行在linux环境中(因为借助了linux信号机制),测试用例经过测试全部可用,测试用例中使用了redis存储任务,可根据自己需要使用文件或者mysql数据库等其他方式存储。
laravel中的消费者使用了sleep的方式间歇性唤醒自己去检查队列是否有可消费的对象,而不用借助于生产者信号唤醒,一定程度上降低了耦合性,生产者只需向任务队列中塞任务即可,但是也会带来一个问题就是如果长久没有任务到来,生产者依然还是不断地要唤醒自己,再挂起自己,这种也是某种形式的浪费资源。但是laravel的例子在任何平台都是可以用的。

本作品采用《CC 协议》,转载必须注明作者和本文链接
每天进步一点点
本帖由系统于 6年前 自动加精
《L04 微信小程序从零到发布》
从小程序个人账户申请开始,带你一步步进行开发一个微信小程序,直到提交微信控制台上线发布。
《L03 构架 API 服务器》
你将学到如 RESTFul 设计风格、PostMan 的使用、OAuth 流程,JWT 概念及使用 和 API 开发相关的进阶知识。
讨论数量: 7

laravel的队列,你这个,还有mq的消息队列有区别吗?

6年前 评论

@梦康 这是个好问题,而且是个大问题,我大概说下我自己的理解吧(不保证权威性正确性):mq=Message Queue就是指消息队列,而我写的是任务队列的模型例子,既然都是队列那么都遵循FIFO,比如都是基于生产者-消费者模型的。消息队列着眼于“消息”的传递,用于解决应用之间的通信问题,比方两个进程间通信,分布式应用间通信;任务队列着眼于“任务”的处理,比方说平常的我们注册了一个网站之后要给用户发邮件,发短信等,这种耗时任务,我们不是很关心及时结果,可以异步执行,这里我们就把这个操作丢到任务队列中处理,任务队列主要的目的就是处理任务。
关于我的这个和 laravel 里面的那个,源码你都可以拿到,我这个只是探究性的小轮子,正如 十个你需要在 PHP 7 中避免的坑 这里第9点说到的:不要重复造轮子,如果应用到项目中,肯定是使用成熟的产品为佳,但是探究性地拆下轮子自己改改,加深对框架的理解也是有好处的,关于二者的最主要区别我也写在上文的结语里面了。

6年前 评论

@wilson_yang 哈,刚好接触mq,有点纳闷。豁然开朗了

6年前 评论

刚好搜索看到这篇问题。其实我早前写了一个开源的任务队列服务HTQ https://github.com/star7th/htq ,其中支持三种不同的任务队列,完美适用于发邮件发消息等任务场景。

5年前 评论

@wilson_yang 顺便再多说一下。我几年前是用swoole扩展来实现任务队列功能的 http://blog.star7th.com/2016/01/1905.html
后来发现太弱了,于是用node写了htq。用node来写居然一百来行代码就实现了三种队列的场景,不得不感慨node在异步这块的强大。由此我也必须承认php的局限性。在任务队列的场合,php实现的性价比很低。还是用合适的工具好,不必局限于php

5年前 评论

@star7th 我这里例程中的代码是没有经过生产应用测试的,这里只是拿 PHP 做了思路描述而已,用尽量少的东西描述大概原理,并没有太多去思考语言层面的东西。

5年前 评论

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!