Laravel 排队的作业即使有延迟也会立即处理
Laravel queued jobs processed immediately even with a delay
我目前正在开发关于私人服务器(例如 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如下:
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() 正在做它应该做的事情。它查询服务器,发送数据包,并用正确的信息更新我的数据库。
您必须在项目根目录的 .env
文件中设置要使用的队列驱动程序。
默认情况下,队列驱动程序是 sync
,它完全按照您的描述执行,立即执行队列。
您可以选择一些不同的队列驱动程序,例如 beanstalked 或 redis(这是我的选择)。 laracasts.com 上有一个关于设置 beanstalked 队列的 excellent freebie。
要查看 Laravel 中所有可用的队列驱动程序选项,请查看 here。
这是一个 .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
要在本地进行测试,您可以将驱动程序设置为
QUEUE_DRIVER=database
和运行phpartisanqueue:table.
然后 php artisan 迁移,这样你就可以将队列保存到数据库中,这样你就可以直观地看到发生了什么.
对于 运行 您的队列,只需 运行 php artisan queue:listen ... 和像 artisan serve
.
一样保留 运行ning
对于已经根据之前的答案进行了更改但仍然无效的人,请检查队列文件的默认值,如下所示:dd(Config::get('queue.default'))
对我来说,直到刷新配置缓存才改变:
php artisan config:clear
确保
'default' => env('QUEUE_DRIVER', 'database'),
在文件 config/queue.php
和
QUEUE_DRIVER=database
在 .env 文件中确保使用数据库驱动程序。
如果您 运行 通过 phpunit 针对队列服务进行测试,请确保
<env name="QUEUE_DRIVER" value="X"/>
在 phpunit.xml 中不会覆盖您想要的队列驱动程序。
如果您运行正在 php artisan serve
上,请重新启动它并再次 运行 php artisan serve
。几个小时后,这对我有用,我想知道它是什么。
:)
在我意识到 Laravel 5.7 在 .env 文件
中将 QUEUE_DRIVER 重命名为 QUEUE_CONNECTION 之前,这让我发疯了很多年
即使您已正确配置所有内容,这仍然可能发生。我们在 Laravel 5.4 中遇到了这个问题,我们创建了很多作业,有些延迟了,然后通过 Queue:bulk($jobs)
将它们添加到队列中。此调用以及 Queue::push($job)
完全忽略 delay
并导致立即处理作业。
如果您希望按照您的配置将作业放入队列,您必须调用 dispatch($job)
。
因为延迟函数取的是未来的绝对日期
UpdateServer::dispatch($server->id)->delay(now()->addSeconds(30))
在我的例子中,我必须实现 ShouldQueue
并使用 Queueable
特性:
class CustomNotification extends Notification implements ShouldQueue{
use Queueable;
...
这是创建用户 API 并将其历史存储在作业 table 中的完整步骤。
在乔布斯 class:
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;
}
}
}
在控制器中 Class:
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 中 Class:
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!');
}
}
}
我目前正在开发关于私人服务器(例如 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如下:
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() 正在做它应该做的事情。它查询服务器,发送数据包,并用正确的信息更新我的数据库。
您必须在项目根目录的 .env
文件中设置要使用的队列驱动程序。
默认情况下,队列驱动程序是 sync
,它完全按照您的描述执行,立即执行队列。
您可以选择一些不同的队列驱动程序,例如 beanstalked 或 redis(这是我的选择)。 laracasts.com 上有一个关于设置 beanstalked 队列的 excellent freebie。
要查看 Laravel 中所有可用的队列驱动程序选项,请查看 here。
这是一个 .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
要在本地进行测试,您可以将驱动程序设置为
QUEUE_DRIVER=database
和运行phpartisanqueue:table.
然后 php artisan 迁移,这样你就可以将队列保存到数据库中,这样你就可以直观地看到发生了什么.
对于 运行 您的队列,只需 运行 php artisan queue:listen ... 和像 artisan serve
.
对于已经根据之前的答案进行了更改但仍然无效的人,请检查队列文件的默认值,如下所示:dd(Config::get('queue.default'))
对我来说,直到刷新配置缓存才改变:
php artisan config:clear
确保
'default' => env('QUEUE_DRIVER', 'database'),
在文件 config/queue.php
和
QUEUE_DRIVER=database
在 .env 文件中确保使用数据库驱动程序。
如果您 运行 通过 phpunit 针对队列服务进行测试,请确保
<env name="QUEUE_DRIVER" value="X"/>
在 phpunit.xml 中不会覆盖您想要的队列驱动程序。
如果您运行正在 php artisan serve
上,请重新启动它并再次 运行 php artisan serve
。几个小时后,这对我有用,我想知道它是什么。
:)
在我意识到 Laravel 5.7 在 .env 文件
中将 QUEUE_DRIVER 重命名为 QUEUE_CONNECTION 之前,这让我发疯了很多年即使您已正确配置所有内容,这仍然可能发生。我们在 Laravel 5.4 中遇到了这个问题,我们创建了很多作业,有些延迟了,然后通过 Queue:bulk($jobs)
将它们添加到队列中。此调用以及 Queue::push($job)
完全忽略 delay
并导致立即处理作业。
如果您希望按照您的配置将作业放入队列,您必须调用 dispatch($job)
。
因为延迟函数取的是未来的绝对日期
UpdateServer::dispatch($server->id)->delay(now()->addSeconds(30))
在我的例子中,我必须实现 ShouldQueue
并使用 Queueable
特性:
class CustomNotification extends Notification implements ShouldQueue{
use Queueable;
...
这是创建用户 API 并将其历史存储在作业 table 中的完整步骤。 在乔布斯 class:
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;
}
}
}
在控制器中 Class:
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 中 Class:
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!');
}
}
}