Laravel延迟任务为何会立即执行?

43

我正在开发一个关于私人服务器(例如Minecraft服务器)的个人应用程序,由于查询服务器需要一些时间,所以我决定实现排队作业。然而,它们没有正常工作,并且即使在延迟时也会立即运行,导致页面请求的巨大延迟。

这是我的HomeController的index()函数,它调用了一个带有30秒延迟的作业来更新每个服务器:

public function index()
{
    $servers = Server::all();

    foreach($servers as $server)
    {
        // Job Dispatch
        $job = (new UpdateServer($server->id))->delay(30);
        $this->dispatch($job);
    }
    return view('serverlist.index', compact('servers'));
}

负责更新服务器的工作类如下:

class UpdateServer extends Job implements SelfHandling, ShouldQueue
{
    use InteractsWithQueue, SerializesModels;
    protected $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

    public function handle(){
        $server = Server::findOrFail($this->id);

        // Preparing the packet
        $test = new RAGBuffer();
        $test->addChar('255');
        $test->addChar('1');
        $test->addShort(1 | 8);

        // Finding the server
        $serverGame = new RAGServer($server->server_ip);

        // Get server information
        $status = $serverGame->sendPacket($test);

        $server->onlinePlayers = $status->getOnline();
        $server->peakPlayers = $status->getPeak();
        $server->maxPlayers = $status->getMax();

        if (!$server->save()) {
            // Error occurred
        }
    }
}

每当HomeController的index()运行时,页面请求会有巨大的延迟。我按照Laravel官方网页上的教程操作,并尝试寻找答案,但是并没有找到。

那么,我做错了什么?为什么我的任务没有被延迟30秒,并在后台进行?

另外:handle()正在执行它应该执行的操作。它查询服务器,发送数据包,并使用正确的信息更新我的数据库。


2
我在使用 dispatch() 时遇到了问题。当我改用 \Queue::later(delay,job) 后,事情开始正常运行。 - QuickDanger
11个回答

52

您需要在项目根目录的.env文件中设置要使用的队列驱动程序。

默认情况下,队列驱动程序是sync,它会立即执行队列,这正是您所描述的。

您可以选择一些不同的队列驱动程序,例如beanstalked或redis(这将是我的选择)。 Laracasts.com上有一个关于设置beanstalked队列的优秀免费视频

要查看Laravel中所有可用的队列驱动程序选项,请参阅此处

下面是一个.env文件的示例:

APP_ENV=local
APP_DEBUG=true
APP_KEY=SomeRandomString

DB_HOST=localhost
DB_DATABASE=homestead
DB_USERNAME=homestead
DB_PASSWORD=secret

CACHE_DRIVER=file
SESSION_DRIVER=file
QUEUE_DRIVER=sync      // <-- Put the desired driver here

MAIL_DRIVER=smtp
MAIL_HOST=mailtrap.io
MAIL_PORT=2525
MAIL_USERNAME=null
MAIL_PASSWORD=null
MAIL_ENCRYPTION=null

1
谢谢回答,我想我会尝试使用beanstalked队列,那么,从我的理解来看,它就像一个后台进程,对页面的加载时间没有影响,是吗? - Micael Sousa
除了带宽(如果您正在使用脚本上传或下载文件),这不应该有任何影响,但这不应该是问题。如果您按照视频操作,Beanstalked 是一个不错的选择,几分钟内即可设置完成。 - baao
很遗憾我没有Laracasts的账户,因为需要它,我会尝试跟随其他教程,一旦我解决了这个问题,我会将您的问题标记为已解决,感谢您的时间! - Micael Sousa
这是另一篇帮助我进行设置的绝佳文章。它提供了逐步配置的步骤。https://scotch.io/tutorials/why-laravel-queues-are-awesome - Dustin

29

在我意识到Laravel 5.7将QUEUE_DRIVER重命名为QUEUE_CONNECTION之前,这让我疯狂了很长一段时间。


4
哦,天啊。谢谢你。我也刚好遇到了同样的事情。 - SaundersB

10

如果您已经按照之前的答案进行更改但仍然无效,请检查队列文件的默认值,方法如下:dd(Config::get('queue.default'))

在我的情况下,只有在清除配置缓存后才能生效:

php artisan config:clear

对于本地开发,重新启动Artisan服务器应该就可以解决问题(我经常会忘记这一点)。 - Abdul Maye

7
为了在本地进行测试,您可以将驱动程序设置为:

QUEUE_DRIVER=database

运行 php artisan queue:table 命令。

然后运行 php artisan migrate 命令,这样你就可以将队列保存在数据库中,并且可以通过可视化界面查看队列的情况。

要运行队列,只需运行命令 php artisan queue:listen ... 就像运行 artisan serve 一样,让它保持运行即可。


需要运行queue:listen命令才能使队列工作!谢谢。 - Gjaa

6
如果您正在运行php artisan serve,请重新启动它并再次运行php artisan serve。在经过数小时的猜测后,这对我有效。:)

3

确保

'default' => env('QUEUE_DRIVER', 'database'), 

在文件config/queue.php

并且

QUEUE_DRIVER=database 

在.env文件中确保使用了数据库驱动程序。


3

如果你要通过 phpunit 对队列服务运行测试,请确保

<env name="QUEUE_DRIVER" value="X"/>

在phpunit.xml中没有覆盖您所需的队列驱动程序。

1
“这是因为延迟函数需要一个将来的绝对日期。”
UpdateServer::dispatch($server->id)->delay(now()->addSeconds(30))

1
在我的情况下,我需要实现ShouldQueue并使用Queueable trait:
class CustomNotification extends Notification implements ShouldQueue{
    use Queueable;
...

0

这是创建用户API并将其历史记录存储在作业表中的完整步骤。 在Jobs类中:

namespace App\Jobs;

use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use App\Repositories\Eloquent\ApiRepo as ApiRepo;
use Log;
use App\Models\User;

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

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

    /**
     * Create a new job instance.
     *
     * @return void
     */
    public $user;

    public function __construct(User $user)
    {
        $this->user = $user; 
    }

    /**
     * Execute the job.
     *
     * @return void
     */
    public function handle()
    {       
        try {
            // make api call
           Log::info("inside handle".$this->user->id);
            $apiRepo = new ApiRepo;
            $response = $apiRepo->getUserDetails($this->user->id);
            Log::info("Response".$response);
        } catch (\Throwable $exception) {
            if ($this->attempts() > 3) {
                // hard fail after 3 attempts
                throw $exception;
            }
            // requeue this job to be executes
            // in 3 minutes (180 seconds) from now
            $this->release(180);
            return;
        }
    }

}

在控制器类中:

namespace App\Http\Controllers\API;

use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Validator;
use App\Models\User;
use App\Jobs\UserProcess;
use App\Models\UserHistory;
use Carbon\Carbon;

class UserController extends Controller
{
    public function create(Request $request)
    {
        $rules = [
            'first_name' => 'required|string|max:100',
            'last_name' => 'required|string|max:100',
            'email' => 'required|string|email|unique:users,email',
            'phone_number' => 'required|string|max:10',
            'address' => 'string',
            'date_of_birth' => 'string|date_format:Y-m-d|before:today',
            'is_vaccinated' => 'string|in:YES,NO',
            'vaccine_name' => 'string|required_if:is_vaccinated,==,YES|in:COVAXIN,COVISHIELD'
        ];

        $validator = Validator::make(array_map('trim', ($request->all())),$rules);

        if($validator->fails()){
            return response()->json($validator->errors());       
        }else{
            $user = new User;
            $user->first_name = $request->first_name;
            $user->last_name = $request->last_name;
            $user->email = $request->email;
            $user->phone_number = $request->phone_number;
            $user->address = $request->address;
            $user->date_of_birth = $request->date_of_birth;
            $user->is_vaccinated = $request->is_vaccinated;
            $user->vaccine_name = $request->vaccine_name;
            $user->save();

            $token = $user->createToken('auth_token')->plainTextToken;
            if($user->save()){
                $job = (new UserProcess($user))->delay(Carbon::now()->addMinutes(1));
                $this->dispatch($job);
            
                return response()
                ->json(['data' => $user,'status' => '200','message' => 'User Added Successfully','access_token' => $token, 'token_type' => 'Bearer']);
            }else{
                return response()
                ->json(['data' => $user,'status' => '409','message' => 'Something went wrong!']);
            }
        }
    } 
}

在 ApiRepo 类中:
namespace App\Repositories\Eloquent;

use App\Repositories\ApiInterface;
use Illuminate\Http\Request;
use App\Http\Requests;
use Illuminate\Http\Response;
use App\Http\Controllers\Controller;
use App\Models\User;
use App\Models\UserHistory;
use Log;

class ApiRepo implements ApiInterface {

     public function getUserDetails($userid) {
        Log::info('User ID - '.@$userid);
        $user_history = new UserHistory();
        $save_user = User::find($userid);
        Log::info('User Json Data - '.@$save_user);
        $user_history->user_id = $userid;
        $user_history->first_name = $save_user->first_name;
        $user_history->last_name = $save_user->last_name;
        $user_history->email = $save_user->email ;
        $user_history->phone_number = $save_user->phone_number;
        $user_history->address = $save_user->address;
        $user_history->date_of_birth = $save_user->date_of_birth;
        $user_history->is_vaccinated = $save_user->is_vaccinated;
        $user_history->vaccine_name = $save_user->vaccine_name;
        $user_history->save();
        if($user_history->save()){
            Log::info('User history Saved!');
        }
     }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接