laravel中队列的使用(一)

2017-08-15 15:28:08

    试想一个场景,你需要从10000个URL中提取网页元素中的title标签。

    你预期的结果只是把这10000个URL一股脑的放入提交框,然后点一下提交,期待程序很好的把这1000个title提取出来然后入库展现出来。

    如果把这10000条数据,一条一条的遍历去执行的话,浏览器一直处于请求状态,不知道何时才能够执行好,这个等待的过程是让人崩溃的,足够让你的客户去怀疑人生,并拍案而起,大吼一声:什么垃圾玩意!

    这个时候,异步+队列这个需求就出来了。我们来看一下使用laravel是如何优雅的解决相应的需求。


    我们首先通过

    php artisan make:controller StartController

    命令生成一个开始任务的控制器,然后在路由中进行注册

    Route::get('/getTitlesByUrl','StartController@index');

    相应StartController.php中初始代码为

<?php
    
    namespace App\Http\Controllers;
    
    use Illuminate\Http\Request;
    
    class StartController extends Controller
    {
        //
        public function index()
        {
            echo "start job";
        }
    }


    我们通过url访问,得到start job的字符串,说明我们控制器已经注册完成,下面我们来开始,我们先模拟数据,创建一个数组,里面放上一些著名的网址当做测试。当然具体数据你可能从excel中获取,或者从txt中获取等,这些暂不再讨论内容。

$weburls     = [
        
        'http://www.baidu.com/',
        'http://www.taobao.com/',
        'http://www.qq.com/',
        'http://www.youku.com/',
        'http://www.muzilong.cn/',
    ];


    然后我们创建一个事件和一个监听器来完成相应的title标签提取,开始的时候,我们需要在app\Providders\EventServiceProvider.php中进行注册

 <?php    
    
        namespace App\Providers;
        
        use Illuminate\Support\Facades\Event;
        use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;
        
        class EventServiceProvider extends ServiceProvider
        {
            /**
             * The event listener mappings for the application.
             *
             * @var array
             */
            protected $listen = [
                'App\Events\getTitlesByUrlEvent' => [
                    'App\Listeners\getTitlesByUrlListener',
                ],
            ];
        
            /**
             * Register any events for your application.
             *
             * @return void
             */
            public function boot()
            {
                parent::boot();
        
                //
            }
        }

     然后命令行执行  

    php artisan event:generate

    生成相应文件,然后我们看到在app文件夹下,生成了Events 和 Listeners文件夹,在这两个文件夹下分别生成了getTitlesByUrlEvent.php 和 getTitlesByUrlListener.php文件


    我们现在只需要把url一个个的传入event中即可,在laravel中,我们可以通过event()这个全局方法,如

    event(new getTitlesByUrlEvent($value));

    接下来我们StartController.php文件中内容变为

    <?php

namespace App\Http\Controllers;

use App\Events\getTitlesByUrlEvent;
use Illuminate\Http\Request;

class StartController extends Controller
{
    //
    public function index()
    {
        $weburls = [

            'http://www.baidu.com/',
            'http://www.taobao.com/',
            'http://www.qq.com/',
            'http://www.youku.com/',
            'http://www.muzilong.cn/',
        ];

        foreach($weburls as $key => $value)
        {
            event(new getTitlesByUrlEvent($value));
        }
    }
}

    getTitlesByUrlEvent.php文件内容为

<?php    
    
    namespace App\Events;
    
    use Illuminate\Broadcasting\Channel;
    use Illuminate\Queue\SerializesModels;
    use Illuminate\Broadcasting\PrivateChannel;
    use Illuminate\Broadcasting\PresenceChannel;
    use Illuminate\Foundation\Events\Dispatchable;
    use Illuminate\Broadcasting\InteractsWithSockets;
    use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
    
    class getTitlesByUrlEvent
    {
        use Dispatchable, InteractsWithSockets, SerializesModels;
    
        public $url;
    
        /**
         * Create a new event instance.
         *
         * @return void
         */
        public function __construct($url)
        {
            //
            $this->url = $url;
        }
    
        /**
         * Get the channels the event should broadcast on.
         *
         * @return Channel|array
         */
        public function broadcastOn()
        {
            return new PrivateChannel('channel-name');
        }
    }

    getTitlesByUrlListener.php内容为:

    <?php

namespace App\Listeners;

use App\Events\getTitlesByUrlEvent;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Support\Facades\Log;

class getTitlesByUrlListener implements ShouldQueue
{
    /**
     * Create the event listener.
     *
     * @return void
     */
    public function __construct()
    {
        //
    }

    /**
     * Handle the event.
     *
     * @param  getTitlesByUrlEvent  $event
     * @return void
     */
    public function handle(getTitlesByUrlEvent $event)
    {
        //
        $url = $event->url;
        Log::info("Now Let's get the title by ".$url);
    }
}

    其中采集url title标签功能暂未完善,先测试一下。注意,我们在监听器中实现了 ShouldQueue 接口,也就是说用到了队列,所以我们需要修改一下相应的配置文件,以及生成相应的任务表,修改.env文件,让QUEUE_DRIVER的值为database,然后在控制台执行


php artisan queue:table
php artisan queue:failed-table
php artisan migrate

    生成相应的表文件,大功告成,这时我们执行一下,并且在控制台执行

php artisan queue:work --daemon

    然后查看LOG日志,如我们预期一样生成了五条记录,非常完美。这样异步,队列的模型就已经搭建完毕了,下面我们来实现剩下的功能。