Laravel - 作业报告失败(即使它正确退出)

Laravel - Job reporting as failed (even though it exits correctly)

我使用的是最新版本的 Homestead。 我还设置了 Laravel Horizo​​n。 我使用 Redis 作为队列驱动程序。 Laravel 是 5.6 版并且是全新安装。

发生的事情是我的作业都失败了(即使作业正确退出)。

我是 运行 通过命令行使用自定义命令的作业:

vagrant@homestead:~/myapp$ artisan crawl:start
vagrant@homestead:~/myapp$ <-- No CLI errors after running

app/Console/Command/crawl.php

<?php

namespace MyApp\Console\Commands;

use Illuminate\Console\Command;
use MyApp\Jobs\Crawl;

class crawl extends Command
{
    /**
     * The name and signature of the console command.
     *
     * @var string
     */
    protected $signature = 'crawl:start';

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = 'Start long running job.';

    /**
     * Create a new command instance.
     *
     * @return void
     */
    public function __construct()
    {
        parent::__construct();
    }

    /**
     * Execute the console command.
     *
     * @return mixed
     */
    public function handle()
    {


        Crawl::dispatch();

    }

}

app/Jobs/Crawl.php

<?php

namespace MyApp\Jobs;

use Illuminate\Bus\Queueable;
use Illuminate\Queue\SerializesModels;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;

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

    /**
     * The number of seconds the job can run before timing out.
     *
     * @var int
     */
    public $timeout = 3600;

    /**
     * The number of times the job may be attempted.
     *
     * @var int
     */
    public $tries = 1;

    /**
     * Create a new job instance.
     *
     * @return void
     */
    public function __construct()
    {

    }

    /**
     * Execute the job.
     *
     * @return void
     */
    public function handle()
    {


        $crawl = new Crawl();
        $crawl->start();


    }
}

app/Crawl.php

<?php
namespace MyApp;

class Crawl
{

    public function start()
    {

        ini_set('memory_limit','256M');
        set_time_limit(3600);

        echo "Started."; 
        sleep(30);
        echo "Exited.";
        exit(); 

    }
}

worker.log

[2018-03-21 10:14:27][1] Processing: MyApp\Jobs\Crawl
Started.
Exited.
[2018-03-21 10:15:59][1] Processing: MyApp\Jobs\Crawl
[2018-03-21 10:15:59][1] Failed:     MyApp\Jobs\Crawl

来自 Horizo​​n 的失败作业详情

Failed At    18-03-21 10:15:59
Error        Illuminate\Queue\MaxAttemptsExceededException:
             MyApp\Jobs\Crawl has been attempted too many 
             times or run too long. The job may have previously 
             timed out. in /home/vagrant/app/vendor/laravel
             /framework/src/Illuminate/Queue/Worker.php:396

laravel-worker.conf

[program:laravel-worker]
process_name=%(program_name)s_%(process_num)02d
command=php /home/vagrant/myapp/artisan queue:work --sleep=3 --tries=1 --timeout=3600
autostart=true
autorestart=true
user=vagrant
numprocs=1
redirect_stderr=true
stdout_logfile=/home/vagrant/myapp/worker.log

config/queue.php

'redis' => [
    'driver' => 'redis',
    'connection' => 'default',
    'queue' => 'default',
    'retry_after' => 90,
    'block_for' => null,
],

.env

QUEUE_DRIVER=redis

剧情简介

查看我的 worker.log 我可以看到我的 class 的输出有效:

Started.
Exited.

但是作业报告失败。为什么? 奇怪的是,同样在 worker.log 中,一份工作显示 Processing 两次:

[2018-03-21 10:15:59][1] Processing: MyApp\Jobs\Crawl
[2018-03-21 10:15:59][1] Failed:     MyApp\Jobs\Crawl

非常感谢任何帮助!

更新

删除 exit() 已解决问题 - 这很奇怪,因为 PHP 手册说您可以使用 exit() 退出程序 "normally":

https://secure.php.net/manual/en/function.exit.php
<?php

//exit program normally
exit;
exit();
exit(0);

Removing the exit() has resolved the issue - this is strange as the PHP manual says that you can use exit() to exit the program "normally"

这对于常规程序是正确的,但是 Laravel 中的排队作业不遵循相同的生命周期。

当队列系统处理作业时,该作业在现有队列工作进程中执行。具体来说,queue worker 从后端获取作业数据,然后调用作业的 handle() 方法。当该方法 returns 时,队列工作程序运行一些代码来完成作业。

如果我们退出作业——通过调用 exit()die() 或触发致命错误——PHP 停止工作进程 运行 作业作为好吧,所以队列系统永远不会完成作业生命周期,并且作业永远不会被标记为 "complete."

我们不需要明确退出作业。如果我们想早点完成工作,我们可以简单地从handle()方法return:

public function handle() 
{
    // ...some code...

    if ($exitEarly) {
        return;
    }

    // ...more code...
}

Laravel 还包括一个特征 InteractsWithQueue,它提供了一个 API 使作业能够自我管理。在这种情况下,我们可以从具有此特征的作业中调用 delete() 方法:

public function handle()
{
    if ($exitEarly) {
        $this->delete();
    }
}

But the job is reported as failed. Why? Strangely, also in the worker.log, it says Processing twice for one job

如上所述,作业无法成功完成,因为我们调用了 exit(),因此队列系统尽职尽责地尝试重试该作业。