Laravel: Hàng đợi (Queues)
Giải phóng thời gian, khai phóng năng lực
- Giới thiệu
- Tạo việc làm
- Phần mềm trung gian việc làm
- Điều phối công việc
- Công việc theo lô
- Xếp hàng đóng cửa
- Chạy Công nhân Hàng đợi
- Cấu hình người giám sát
- Xử lý công việc thất bại
- Xóa công việc khỏi hàng đợi
- Giám sát hàng đợi của bạn
- Sự kiện việc làm
Giới thiệu
Trong khi xây dựng ứng dụng web của mình, bạn có thể có một số tác vụ, chẳng hạn như phân tích cú pháp và lưu trữ tệp CSV đã tải lên, mất quá nhiều thời gian để thực hiện trong một yêu cầu web thông thường. Rất may, Laravel cho phép bạn dễ dàng tạo các công việc được xếp hàng đợi có thể được xử lý ở chế độ nền. Bằng cách chuyển các tác vụ tốn nhiều thời gian vào một hàng đợi, ứng dụng của bạn có thể phản hồi các yêu cầu web với tốc độ nhanh chóng và cung cấp trải nghiệm người dùng tốt hơn cho khách hàng của bạn.
Hàng đợi Laravel cung cấp một API xếp hàng thống nhất trên nhiều loại phụ trợ hàng đợi khác nhau, chẳng hạn như Amazon SQS , Redis hoặc thậm chí là cơ sở dữ liệu quan hệ.
Các tùy chọn cấu hình hàng đợi của Laravel được lưu trữ trong config/queue.php
tệp cấu hình ứng dụng của bạn . Trong tệp này, bạn sẽ tìm thấy cấu hình kết nối cho từng trình điều khiển hàng đợi được bao gồm trong khuôn khổ, bao gồm cơ sở dữ liệu, trình điều khiển Amazon SQS , Redis và Beanstalkd , cũng như trình điều khiển đồng bộ sẽ thực thi công việc ngay lập tức (để sử dụng trong phát triển địa phương). Một null
tài xế đợi cũng được bao gồm trong đó vứt bỏ xếp hàng đợi công việc.
Laravel hiện cung cấp Horizon, một hệ thống cấu hình và bảng điều khiển đẹp mắt cho các hàng đợi được hỗ trợ bởi Redis của bạn. Kiểm tra toàn bộ tài liệu Horizon để biết thêm thông tin.
Kết nối Vs. Hàng đợi
Trước khi bắt đầu với hàng đợi Laravel, điều quan trọng là phải hiểu sự phân biệt giữa "kết nối" và "hàng đợi". Trong config/queue.php
tệp cấu hình của bạn , có một connections
mảng cấu hình. Tùy chọn này xác định các kết nối đến các dịch vụ hàng đợi phụ trợ như Amazon SQS, Beanstalk hoặc Redis. Tuy nhiên, bất kỳ kết nối hàng đợi nhất định nào cũng có thể có nhiều "hàng đợi" có thể được coi là các ngăn xếp hoặc đống công việc được xếp hàng khác nhau.
Lưu ý rằng mỗi ví dụ cấu hình kết nối trong queue
tệp cấu hình chứa một queue
thuộc tính. Đây là hàng đợi mặc định mà các công việc sẽ được gửi đến khi chúng được gửi đến một kết nối nhất định. Nói cách khác, nếu bạn gửi một công việc mà không xác định rõ ràng nó sẽ được gửi đến hàng đợi nào, công việc sẽ được đặt trên hàng đợi được xác định trong queue
thuộc tính của cấu hình kết nối:
use App\Jobs\ProcessPodcast;
// This job is sent to the default connection's default queue...
ProcessPodcast::dispatch();
// This job is sent to the default connection's "emails" queue...
ProcessPodcast::dispatch()->onQueue('emails');
Một số ứng dụng có thể không cần đẩy công việc lên nhiều hàng đợi, thay vào đó chỉ thích có một hàng đợi đơn giản. Tuy nhiên, việc đẩy các công việc đến nhiều hàng đợi có thể đặc biệt hữu ích cho các ứng dụng muốn ưu tiên hoặc phân đoạn cách xử lý các công việc, vì Laravel queue worker cho phép bạn chỉ định các hàng đợi mà nó sẽ xử lý theo mức độ ưu tiên. Ví dụ: nếu bạn đẩy các công việc vào một high
hàng đợi, bạn có thể chạy một worker cung cấp cho chúng mức độ ưu tiên xử lý cao hơn:
php artisan queue:work --queue=high,default
Ghi chú và điều kiện tiên quyết của trình điều khiển
Cơ sở dữ liệu
Để sử dụng database
trình điều khiển hàng đợi, bạn sẽ cần một bảng cơ sở dữ liệu để chứa các công việc. Để tạo một di chuyển tạo bảng này, hãy chạy queue:table
lệnh Artisan. Khi quá trình di chuyển đã được tạo, bạn có thể di chuyển cơ sở dữ liệu của mình bằng migrate
lệnh:
php artisan queue:table
php artisan migrate
Redis
Để sử dụng redis
trình điều khiển hàng đợi, bạn nên định cấu hình kết nối cơ sở dữ liệu Redis trong config/database.php
tệp cấu hình của mình .
Cụm Redis
Nếu kết nối hàng đợi Redis của bạn sử dụng Cụm Redis, tên hàng đợi của bạn phải chứa thẻ băm khóa . Điều này là bắt buộc để đảm bảo tất cả các khóa Redis cho một hàng đợi nhất định được đặt vào cùng một vị trí băm:
'redis' => [
'driver' => 'redis',
'connection' => 'default',
'queue' => '{default}',
'retry_after' => 90,
],
Chặn
Khi sử dụng hàng đợi Redis, bạn có thể sử dụng block_for
tùy chọn cấu hình để chỉ định thời gian trình điều khiển sẽ đợi công việc khả dụng trước khi lặp qua vòng lặp worker và thăm dò lại cơ sở dữ liệu Redis.
Điều chỉnh giá trị này dựa trên tải hàng đợi của bạn có thể hiệu quả hơn so với việc liên tục thăm dò cơ sở dữ liệu Redis cho các công việc mới. Ví dụ: bạn có thể đặt giá trị để 5
chỉ ra rằng trình điều khiển sẽ chặn trong năm giây trong khi chờ công việc sẵn sàng:
'redis' => [
'driver' => 'redis',
'connection' => 'default',
'queue' => 'default',
'retry_after' => 90,
'block_for' => 5,
],
Đặt
block_for
thành0
sẽ khiến nhân viên hàng đợi chặn vô thời hạn cho đến khi có công việc. Điều này cũng sẽ ngăn các tín hiệu nhưSIGTERM
được xử lý cho đến khi công việc tiếp theo được xử lý.
Các điều kiện tiên quyết về trình điều khiển khác
Các phụ thuộc sau đây là cần thiết cho các trình điều khiển hàng đợi được liệt kê. Các phần phụ thuộc này có thể được cài đặt thông qua trình quản lý gói Composer:
- Amazon SQS:
aws/aws-sdk-php ~3.0
- Beanstalkd:
pda/pheanstalk ~4.0
- Redis:
predis/predis ~1.0
hoặc phpredis PHP extension
Tạo việc làm
Tạo các lớp công việc
Theo mặc định, tất cả các công việc có thể xếp hàng đợi cho ứng dụng của bạn được lưu trữ trong app/Jobs
thư mục. Nếu app/Jobs
thư mục không tồn tại, nó sẽ được tạo khi bạn chạy make:job
lệnh Artisan:
php artisan make:job ProcessPodcast
Lớp được tạo sẽ triển khai Illuminate\Contracts\Queue\ShouldQueue
giao diện, chỉ ra cho Laravel biết rằng công việc nên được đẩy vào hàng đợi để chạy không đồng bộ.
Cấu trúc lớp học
Các lớp công việc rất đơn giản, thường chỉ chứa một handle
phương thức được gọi khi công việc được xử lý bởi hàng đợi. Để bắt đầu, chúng ta hãy xem một lớp công việc ví dụ. Trong ví dụ này, chúng tôi sẽ giả sử chúng tôi quản lý một dịch vụ xuất bản podcast và cần xử lý các tệp podcast đã tải lên trước khi chúng được xuất bản:
<?php
namespace App\Jobs;
use App\Models\Podcast;
use App\Services\AudioProcessor;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
class ProcessPodcast implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
/**
* The podcast instance.
*
* @var \App\Models\Podcast
*/
protected $podcast;
/**
* Create a new job instance.
*
* @param App\Models\Podcast $podcast
* @return void
*/
public function __construct(Podcast $podcast)
{
$this->podcast = $podcast;
}
/**
* Execute the job.
*
* @param App\Services\AudioProcessor $processor
* @return void
*/
public function handle(AudioProcessor $processor)
{
// Process uploaded podcast...
}
}
Trong ví dụ này, hãy lưu ý rằng chúng ta có thể truyền trực tiếp một mô hình Eloquent vào hàm tạo của công việc được xếp hàng đợi. Do SerializesModels
đặc điểm mà công việc đang sử dụng, các mô hình Eloquent và các mối quan hệ đã được tải của chúng sẽ được tuần tự hóa một cách duyên dáng và không được sử dụng hóa khi công việc đang được xử lý.
Nếu công việc được xếp hàng đợi của bạn chấp nhận một mô hình Eloquent trong phương thức khởi tạo của nó, thì chỉ mã định danh cho mô hình đó mới được tuần tự hóa vào hàng đợi. Khi công việc thực sự được xử lý, hệ thống hàng đợi sẽ tự động truy xuất lại cá thể mô hình đầy đủ và các mối quan hệ đã tải của nó từ cơ sở dữ liệu. Cách tiếp cận tuần tự hóa mô hình này cho phép gửi khối lượng công việc nhỏ hơn nhiều đến trình điều khiển hàng đợi của bạn.
handle
Phương pháp tiêm phụ thuộc
Các handle
phương pháp được gọi khi công việc được xử lý bởi các hàng đợi. Lưu ý rằng chúng ta có thể gõ-gợi ý phụ thuộc vào handle
phương thức của công việc. Vùng chứa dịch vụ Laravel tự động đưa các phụ thuộc này vào.
Nếu bạn muốn toàn quyền kiểm soát cách vùng chứa đưa các phụ thuộc vào handle
phương thức, bạn có thể sử dụng bindMethod
phương thức của vùng chứa . Các bindMethod
phương pháp chấp nhận một cuộc gọi lại mà nhận công việc và container. Trong lệnh gọi lại, bạn có thể tự do gọi handle
phương thức theo bất kỳ cách nào bạn muốn. Thông thường, bạn nên gọi phương thức này từ boot
phương thức của App\Providers\AppServiceProvider
nhà cung cấp dịch vụ của bạn :
use App\Jobs\ProcessPodcast;
use App\Services\AudioProcessor;
$this->app->bindMethod([ProcessPodcast::class, 'handle'], function ($job, $app) {
return $job->handle($app->make(AudioProcessor::class));
});
Dữ liệu nhị phân, chẳng hạn như nội dung hình ảnh thô, phải được chuyển qua
base64_encode
hàm trước khi được chuyển đến công việc được xếp hàng đợi. Nếu không, công việc có thể không được tuần tự hóa thành JSON một cách chính xác khi được đưa vào hàng đợi.
Xử lý các mối quan hệ
Bởi vì các mối quan hệ được tải cũng được tuần tự hóa, chuỗi công việc được tuần tự hóa đôi khi có thể trở nên khá lớn. Để ngăn các quan hệ bị tuần tự hóa, bạn có thể gọi withoutRelations
phương thức trên mô hình khi đặt giá trị thuộc tính. Phương thức này sẽ trả về một thể hiện của mô hình mà không có các mối quan hệ được tải của nó:
/**
* Create a new job instance.
*
* @param \App\Models\Podcast $podcast
* @return void
*/
public function __construct(Podcast $podcast)
{
$this->podcast = $podcast->withoutRelations();
}
Việc làm duy nhất
Các công việc duy nhất yêu cầu trình điều khiển bộ nhớ cache hỗ trợ khóa . Hiện nay,
memcached
,redis
,dynamodb
,database
,file
, vàarray
trình điều khiển bộ nhớ cache hỗ trợ ổ khóa nguyên tử. Ngoài ra, các ràng buộc công việc duy nhất không áp dụng cho các công việc theo lô.
Đôi khi, bạn có thể muốn đảm bảo rằng chỉ có một trường hợp của một công việc cụ thể có trong hàng đợi tại bất kỳ thời điểm nào. Bạn có thể làm như vậy bằng cách triển khai ShouldBeUnique
giao diện trên lớp công việc của mình. Giao diện này không yêu cầu bạn xác định bất kỳ phương thức bổ sung nào trên lớp của bạn:
<?php
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Contracts\Queue\ShouldBeUnique;
class UpdateSearchIndex implements ShouldQueue, ShouldBeUnique
{
...
}
Trong ví dụ trên, UpdateSearchIndex
công việc là duy nhất. Vì vậy, công việc sẽ không được gửi đi nếu một phiên bản khác của công việc đã có trong hàng đợi và chưa hoàn tất quá trình xử lý.
Trong một số trường hợp nhất định, bạn có thể muốn xác định một "chìa khóa" cụ thể làm cho công việc trở thành duy nhất hoặc bạn có thể muốn chỉ định thời gian chờ mà sau đó công việc không còn là duy nhất. Để thực hiện điều này, bạn có thể định nghĩa uniqueId
và uniqueFor
các thuộc tính hoặc phương thức trên lớp công việc của mình:
<?php
use App\Product;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Contracts\Queue\ShouldBeUnique;
class UpdateSearchIndex implements ShouldQueue, ShouldBeUnique
{
/**
* The product instance.
*
* @var \App\Product
*/
public $product;
/**
* The number of seconds after which the job's unique lock will be released.
*
* @var int
*/
public $uniqueFor = 3600;
/**
* The unique ID of the job.
*
* @return string
*/
public function uniqueId()
{
return $this->product->id;
}
}
Trong ví dụ trên, UpdateSearchIndex
công việc là duy nhất bởi một ID sản phẩm. Vì vậy, bất kỳ công việc mới nào có cùng ID sản phẩm sẽ bị bỏ qua cho đến khi công việc hiện tại hoàn tất quá trình xử lý. Ngoài ra, nếu công việc hiện tại không được xử lý trong vòng một giờ, khóa duy nhất sẽ được giải phóng và một công việc khác có cùng khóa duy nhất có thể được gửi đến hàng đợi.
Giữ công việc duy nhất cho đến khi bắt đầu xử lý
Theo mặc định, các công việc duy nhất được "mở khóa" sau khi một công việc hoàn tất quá trình xử lý hoặc không thực hiện được tất cả các lần thử lại của nó. Tuy nhiên, có thể có những tình huống mà bạn muốn công việc của mình mở khóa ngay lập tức trước khi nó được xử lý. Để thực hiện điều này, công việc của bạn nên thực hiện ShouldBeUniqueUntilProcessing
hợp đồng thay vì ShouldBeUnique
hợp đồng:
<?php
use App\Product;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Contracts\Queue\ShouldBeUniqueUntilProcessing;
class UpdateSearchIndex implements ShouldQueue, ShouldBeUniqueUntilProcessing
{
// ...
}
Khoá việc làm duy nhất
Phía sau hậu trường, khi một ShouldBeUnique
công việc được cử đi, Laravel cố gắng lấy một ổ khóa bằng uniqueId
chìa khóa. Nếu không có được khóa, công việc sẽ không được thực hiện. Khóa này được giải phóng khi công việc hoàn tất xử lý hoặc không thực hiện được tất cả các lần thử lại. Theo mặc định, Laravel sẽ sử dụng trình điều khiển bộ đệm mặc định để lấy khóa này. Tuy nhiên, nếu bạn muốn sử dụng một trình điều khiển khác để có được khóa, bạn có thể xác định một uniqueVia
phương pháp trả về trình điều khiển bộ đệm sẽ được sử dụng:
use Illuminate\Support\Facades\Cache;
class UpdateSearchIndex implements ShouldQueue, ShouldBeUnique
{
...
/**
* Get the cache driver for the unique job lock.
*
* @return \Illuminate\Contracts\Cache\Repository
*/
public function uniqueVia()
{
return Cache::driver('redis');
}
}
Nếu bạn chỉ cần giới hạn việc xử lý đồng thời một công việc, hãy sử dụng
WithoutOverlapping
phần mềm trung gian công việc để thay thế.
Phần mềm trung gian việc làm
Phần mềm trung gian công việc cho phép bạn bao bọc logic tùy chỉnh xung quanh việc thực hiện các công việc được xếp hàng đợi, giảm bớt việc viết sẵn trong chính các công việc đó. Ví dụ: hãy xem xét handle
phương pháp sau sử dụng các tính năng giới hạn tốc độ Redis của Laravel để chỉ cho phép một công việc xử lý sau mỗi năm giây:
use Illuminate\Support\Facades\Redis;
/**
* Execute the job.
*
* @return void
*/
public function handle()
{
Redis::throttle('key')->block(0)->allow(1)->every(5)->then(function () {
info('Lock obtained...');
// Handle job...
}, function () {
// Could not obtain lock...
return $this->release(5);
});
}
Trong khi mã này hợp lệ, việc triển khai handle
phương thức trở nên ồn ào vì nó lộn xộn với logic giới hạn tốc độ Redis. Ngoài ra, logic giới hạn tỷ lệ này phải được sao chép cho bất kỳ công việc nào khác mà chúng tôi muốn giới hạn tỷ lệ.
Thay vì giới hạn tốc độ trong phương pháp xử lý, chúng tôi có thể xác định một phần mềm trung gian công việc xử lý giới hạn tốc độ. Laravel không có vị trí mặc định cho phần mềm trung gian công việc, vì vậy bạn có thể đặt phần mềm trung gian việc làm ở bất kỳ đâu trong ứng dụng của mình. Trong ví dụ này, chúng tôi sẽ đặt phần mềm trung gian trong một app/Jobs/Middleware
thư mục:
<?php
namespace App\Jobs\Middleware;
use Illuminate\Support\Facades\Redis;
class RateLimited
{
/**
* Process the queued job.
*
* @param mixed $job
* @param callable $next
* @return mixed
*/
public function handle($job, $next)
{
Redis::throttle('key')
->block(0)->allow(1)->every(5)
->then(function () use ($job, $next) {
// Lock obtained...
$next($job);
}, function () use ($job) {
// Could not obtain lock...
$job->release(5);
});
}
}
Như bạn có thể thấy, giống như phần mềm trung gian định tuyến , phần mềm trung gian công việc nhận công việc đang được xử lý và một lệnh gọi lại sẽ được gọi để tiếp tục xử lý công việc.
Sau khi tạo phần mềm trung gian công việc, họ có thể gắn bó với một công việc bằng cách trả lại chúng từ middleware
phương thức của công việc . Phương thức này không tồn tại trên các công việc được tạo bởi make:job
lệnh Artisan, vì vậy bạn sẽ cần phải thêm nó vào lớp công việc của mình theo cách thủ công:
use App\Jobs\Middleware\RateLimited;
/**
* Get the middleware the job should pass through.
*
* @return array
*/
public function middleware()
{
return [new RateLimited];
}
Phần mềm trung gian công việc cũng có thể được chỉ định cho người nghe sự kiện có thể xếp hàng đợi, mailables và thông báo.
Giới hạn tỷ lệ
Mặc dù chúng tôi vừa trình bày cách viết phần mềm trung gian giới hạn tỷ lệ công việc của riêng bạn, Laravel thực sự bao gồm phần mềm trung gian giới hạn tỷ lệ mà bạn có thể sử dụng để xếp hạng các công việc giới hạn. Giống như bộ giới hạn tỷ lệ tuyến đường , bộ giới hạn tỷ lệ công việc được xác định bằng cách sử dụng phương pháp RateLimiter
của mặt tiền for
.
Ví dụ: bạn có thể muốn cho phép người dùng sao lưu dữ liệu của họ một lần mỗi giờ trong khi không áp đặt giới hạn như vậy đối với khách hàng cao cấp. Để thực hiện điều này, bạn có thể xác định một RateLimiter
trong boot
phương thức của bạn AppServiceProvider
:
use Illuminate\Cache\RateLimiting\Limit;
use Illuminate\Support\Facades\RateLimiter;
/**
* Bootstrap any application services.
*
* @return void
*/
public function boot()
{
RateLimiter::for('backups', function ($job) {
return $job->user->vipCustomer()
? Limit::none()
: Limit::perHour(1)->by($job->user->id);
});
}
Trong ví dụ trên, chúng tôi đã xác định giới hạn tỷ lệ hàng giờ; tuy nhiên, bạn có thể dễ dàng xác định giới hạn tỷ lệ dựa trên số phút bằng cách sử dụng perMinute
phương pháp này. Ngoài ra, bạn có thể chuyển bất kỳ giá trị nào bạn muốn vào by
phương pháp giới hạn tỷ lệ; tuy nhiên, giá trị này thường được sử dụng nhất để phân đoạn giới hạn tỷ lệ theo khách hàng:
return Limit::perMinute(50)->by($job->user->id);
Khi bạn đã xác định giới hạn tốc độ của mình, bạn có thể gắn bộ giới hạn tốc độ vào công việc sao lưu của mình bằng cách sử dụng Illuminate\Queue\Middleware\RateLimited
phần mềm trung gian. Mỗi khi công việc vượt quá giới hạn tốc độ, phần mềm trung gian này sẽ giải phóng công việc trở lại hàng đợi với độ trễ thích hợp dựa trên khoảng thời gian giới hạn tốc độ.
use Illuminate\Queue\Middleware\RateLimited;
/**
* Get the middleware the job should pass through.
*
* @return array
*/
public function middleware()
{
return [new RateLimited('backups')];
}
Việc trả lại một công việc bị giới hạn tỷ lệ vào hàng đợi sẽ vẫn làm tăng tổng số công việc của attempts
. Bạn có thể muốn điều chỉnh của bạn tries
và maxExceptions
các thuộc tính trên hạng công việc của bạn cho phù hợp. Hoặc, bạn có thể muốn sử dụng retryUntil
phương pháp này để xác định khoảng thời gian cho đến khi công việc không còn được thực hiện nữa.
Nếu bạn không muốn một công việc được thử lại khi nó bị giới hạn tốc độ, bạn có thể sử dụng dontRelease
phương pháp:
/**
* Get the middleware the job should pass through.
*
* @return array
*/
public function middleware()
{
return [(new RateLimited('backups'))->dontRelease()];
}
Nếu bạn đang sử dụng Redis, bạn có thể sử dụng
Illuminate\Queue\Middleware\RateLimitedWithRedis
phần mềm trung gian, được tinh chỉnh cho Redis và hiệu quả hơn phần mềm trung gian giới hạn tốc độ cơ bản.
Ngăn chặn trùng lặp công việc
Laravel bao gồm một Illuminate\Queue\Middleware\WithoutOverlapping
phần mềm trung gian cho phép bạn ngăn chặn sự chồng chéo công việc dựa trên một khóa tùy ý. Điều này có thể hữu ích khi một công việc được xếp hàng đợi đang sửa đổi một tài nguyên mà chỉ nên được sửa đổi bởi một công việc tại một thời điểm.
Ví dụ: hãy tưởng tượng bạn có một công việc được xếp hàng đợi cập nhật điểm tín dụng của người dùng và bạn muốn ngăn chồng chéo công việc cập nhật điểm tín dụng cho cùng một ID người dùng. Để thực hiện điều này, bạn có thể trả lại WithoutOverlapping
phần mềm trung gian từ middleware
phương thức công việc của bạn :
use Illuminate\Queue\Middleware\WithoutOverlapping;
/**
* Get the middleware the job should pass through.
*
* @return array
*/
public function middleware()
{
return [new WithoutOverlapping($this->user->id)];
}
Mọi công việc chồng chéo sẽ được đưa trở lại hàng đợi. Bạn cũng có thể chỉ định số giây phải trôi qua trước khi công việc được giải phóng sẽ được thử lại:
/**
* Get the middleware the job should pass through.
*
* @return array
*/
public function middleware()
{
return [(new WithoutOverlapping($this->order->id))->releaseAfter(60)];
}
Nếu bạn muốn xóa ngay lập tức bất kỳ công việc chồng chéo nào để chúng không bị thử lại, bạn có thể sử dụng dontRelease
phương pháp:
/**
* Get the middleware the job should pass through.
*
* @return array
*/
public function middleware()
{
return [(new WithoutOverlapping($this->order->id))->dontRelease()];
}
Phần WithoutOverlapping
mềm trung gian được cung cấp bởi tính năng khóa nguyên tử của Laravel. Đôi khi, công việc của bạn có thể bị lỗi bất ngờ hoặc hết thời gian khiến khóa không được mở ra. Do đó, bạn có thể xác định rõ ràng thời gian hết hạn của khóa bằng expireAfter
phương pháp này. Ví dụ, ví dụ dưới đây sẽ hướng dẫn Laravel giải phóng WithoutOverlapping
khóa ba phút sau khi công việc bắt đầu xử lý:
/**
* Get the middleware the job should pass through.
*
* @return array
*/
public function middleware()
{
return [(new WithoutOverlapping($this->order->id))->expireAfter(180)];
}
Phần
WithoutOverlapping
mềm trung gian yêu cầu trình điều khiển bộ nhớ cache hỗ trợ khóa . Hiện nay,memcached
,redis
,dynamodb
,database
,file
, vàarray
trình điều khiển bộ nhớ cache hỗ trợ ổ khóa nguyên tử.
Ngoại lệ điều chỉnh
Laravel bao gồm một Illuminate\Queue\Middleware\ThrottlesExceptions
phần mềm trung gian cho phép bạn điều chỉnh các ngoại lệ. Khi công việc ném ra một số ngoại lệ nhất định, tất cả các nỗ lực tiếp theo để thực hiện công việc sẽ bị trì hoãn cho đến khi một khoảng thời gian cụ thể hết hiệu lực. Phần mềm trung gian này đặc biệt hữu ích cho các công việc tương tác với các dịch vụ của bên thứ ba không ổn định.
Ví dụ: hãy tưởng tượng một công việc được xếp hàng đợi tương tác với API của bên thứ ba bắt đầu đưa ra các ngoại lệ. Để giảm bớt các ngoại lệ, bạn có thể trả lại ThrottlesExceptions
phần mềm trung gian từ middleware
phương thức công việc của mình . Thông thường, phần mềm trung gian này nên được ghép nối với một công việc thực hiện các nỗ lực dựa trên thời gian :
use Illuminate\Queue\Middleware\ThrottlesExceptions;
/**
* Get the middleware the job should pass through.
*
* @return array
*/
public function middleware()
{
return [new ThrottlesExceptions(10, 5)];
}
/**
* Determine the time at which the job should timeout.
*
* @return \DateTime
*/
public function retryUntil()
{
return now()->addMinutes(5);
}
Đối số phương thức khởi tạo đầu tiên được phần mềm trung gian chấp nhận là số lượng ngoại lệ mà công việc có thể ném ra trước khi được điều chỉnh, trong khi đối số phương thức khởi tạo thứ hai là số phút sẽ trôi qua trước khi công việc được thử lại sau khi nó đã được điều chỉnh. Trong ví dụ mã ở trên, nếu công việc ném 10 ngoại lệ trong vòng 5 phút, chúng tôi sẽ đợi 5 phút trước khi thử lại công việc.
Khi một công việc đưa ra một ngoại lệ nhưng vẫn chưa đạt đến ngưỡng ngoại lệ, công việc thường sẽ được thử lại ngay lập tức. Tuy nhiên, bạn có thể chỉ định số phút mà công việc đó sẽ bị trì hoãn bằng cách gọi backoff
phương thức khi đính kèm phần mềm trung gian vào công việc:
use Illuminate\Queue\Middleware\ThrottlesExceptions;
/**
* Get the middleware the job should pass through.
*
* @return array
*/
public function middleware()
{
return [(new ThrottlesExceptions(10, 5))->backoff(5)];
}
Bên trong, phần mềm trung gian này sử dụng hệ thống bộ nhớ cache của Laravel để thực hiện giới hạn tốc độ và tên lớp của công việc được sử dụng làm "khóa" bộ nhớ cache. Bạn có thể ghi đè khóa này bằng cách gọi by
phương thức khi gắn phần mềm trung gian vào công việc của bạn. Điều này có thể hữu ích nếu bạn có nhiều công việc tương tác với cùng một dịch vụ của bên thứ ba và bạn muốn chúng chia sẻ một "nhóm" điều tiết chung:
use Illuminate\Queue\Middleware\ThrottlesExceptions;
/**
* Get the middleware the job should pass through.
*
* @return array
*/
public function middleware()
{
return [(new ThrottlesExceptions(10, 10))->by('key')];
}
Nếu bạn đang sử dụng Redis, bạn có thể sử dụng
Illuminate\Queue\Middleware\ThrottlesExceptionsWithRedis
phần mềm trung gian, được tinh chỉnh cho Redis và hiệu quả hơn phần mềm trung gian điều chỉnh ngoại lệ cơ bản.
Điều phối công việc
Khi bạn đã viết lớp công việc của mình, bạn có thể gửi nó bằng cách sử dụng dispatch
phương pháp trên chính công việc đó. Các đối số được truyền cho dispatch
phương thức sẽ được cấp cho hàm tạo của công việc:
<?php
namespace App\Http\Controllers;
use App\Http\Controllers\Controller;
use App\Jobs\ProcessPodcast;
use App\Models\Podcast;
use Illuminate\Http\Request;
class PodcastController extends Controller
{
/**
* Store a new podcast.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function store(Request $request)
{
$podcast = Podcast::create(...);
// ...
ProcessPodcast::dispatch($podcast);
}
}
Nếu bạn muốn điều động một công việc có điều kiện, bạn có thể sử dụng các phương pháp dispatchIf
và dispatchUnless
:
ProcessPodcast::dispatchIf($accountActive, $podcast);
ProcessPodcast::dispatchUnless($accountSuspended, $podcast);
Gửi chậm
Nếu bạn muốn chỉ định rằng một công việc không có sẵn ngay lập tức để xử lý bởi nhân viên hàng đợi, bạn có thể sử dụng delay
phương pháp này khi điều động công việc. Ví dụ: hãy xác định rằng một công việc sẽ không có sẵn để xử lý cho đến 10 phút sau khi nó đã được gửi đi:
<?php
namespace App\Http\Controllers;
use App\Http\Controllers\Controller;
use App\Jobs\ProcessPodcast;
use App\Models\Podcast;
use Illuminate\Http\Request;
class PodcastController extends Controller
{
/**
* Store a new podcast.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function store(Request $request)
{
$podcast = Podcast::create(...);
// ...
ProcessPodcast::dispatch($podcast)
->delay(now()->addMinutes(10));
}
}
Dịch vụ xếp hàng Amazon SQS có thời gian trễ tối đa là 15 phút.
Gửi sau khi phản hồi được gửi tới trình duyệt
Ngoài ra, dispatchAfterResponse
phương pháp này sẽ trì hoãn việc gửi một công việc cho đến sau khi phản hồi HTTP được gửi đến trình duyệt của người dùng. Điều này sẽ vẫn cho phép người dùng bắt đầu sử dụng ứng dụng ngay cả khi một công việc được xếp hàng đợi vẫn đang thực hiện. Điều này thường chỉ được sử dụng cho các công việc mất khoảng một giây, chẳng hạn như gửi email. Vì chúng được xử lý trong yêu cầu HTTP hiện tại, các công việc được gửi theo kiểu này không yêu cầu nhân viên hàng đợi đang chạy để chúng được xử lý:
use App\Jobs\SendNotification;
SendNotification::dispatchAfterResponse();
Bạn cũng có thể dispatch
đóng và xâu chuỗi afterResponse
phương thức vào trình dispatch
trợ giúp để thực hiện đóng sau khi phản hồi HTTP đã được gửi đến trình duyệt:
use App\Mail\WelcomeMessage;
use Illuminate\Support\Facades\Mail;
dispatch(function () {
Mail::to('taylor@example.com')->send(new WelcomeMessage);
})->afterResponse();
Điều phối đồng bộ
Nếu bạn muốn gửi một công việc ngay lập tức (đồng bộ), bạn có thể sử dụng dispatchSync
phương pháp này. Khi sử dụng phương pháp này, công việc sẽ không được xếp hàng đợi và sẽ được thực hiện ngay lập tức trong quy trình hiện tại:
<?php
namespace App\Http\Controllers;
use App\Http\Controllers\Controller;
use App\Jobs\ProcessPodcast;
use App\Models\Podcast;
use Illuminate\Http\Request;
class PodcastController extends Controller
{
/**
* Store a new podcast.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function store(Request $request)
{
$podcast = Podcast::create(...);
// Create podcast...
ProcessPodcast::dispatchSync($podcast);
}
}
Giao dịch Công việc & Cơ sở dữ liệu
Mặc dù hoàn toàn ổn khi gửi công việc trong các giao dịch cơ sở dữ liệu, nhưng bạn nên đặc biệt lưu ý để đảm bảo rằng công việc của bạn sẽ thực sự có thể thực hiện thành công. Khi điều động một công việc trong một giao dịch, có thể công việc đó sẽ được xử lý bởi một công nhân trước khi giao dịch đã cam kết. Khi điều này xảy ra, bất kỳ cập nhật nào bạn đã thực hiện cho các mô hình hoặc bản ghi cơ sở dữ liệu trong quá trình giao dịch cơ sở dữ liệu có thể chưa được phản ánh trong cơ sở dữ liệu. Ngoài ra, bất kỳ mô hình hoặc bản ghi cơ sở dữ liệu nào được tạo trong giao dịch có thể không tồn tại trong cơ sở dữ liệu.
Rất may, Laravel cung cấp một số phương pháp giải quyết vấn đề này. Đầu tiên, bạn có thể đặt after_commit
tùy chọn kết nối trong mảng cấu hình kết nối hàng đợi của mình:
'redis' => [
'driver' => 'redis',
// ...
'after_commit' => true,
],
Khi có after_commit
tùy chọn này true
, bạn có thể điều phối các công việc trong các giao dịch cơ sở dữ liệu; tuy nhiên, Laravel sẽ đợi cho đến khi tất cả các giao dịch cơ sở dữ liệu mở đã được cam kết trước khi thực sự điều động công việc. Tất nhiên, nếu không có giao dịch cơ sở dữ liệu nào hiện đang mở, công việc sẽ được điều động ngay lập tức.
Nếu một giao dịch bị lùi lại do một ngoại lệ xảy ra trong giao dịch, các công việc đã điều động đã được điều động trong giao dịch đó sẽ bị loại bỏ.
Đặt
after_commit
tùy chọn cấu hình thànhtrue
cũng sẽ khiến mọi trình nghe sự kiện, mailables, thông báo và sự kiện truyền phát được xếp hàng đợi sẽ được gửi đi sau khi tất cả các giao dịch cơ sở dữ liệu mở đã được cam kết.
Chỉ định Nội tuyến Hành vi Công văn Cam kết
Nếu bạn không đặt after_commit
tùy chọn cấu hình kết nối hàng đợi thành true
, bạn vẫn có thể chỉ ra rằng một công việc cụ thể nên được gửi đi sau khi tất cả các giao dịch cơ sở dữ liệu mở đã được cam kết. Để thực hiện điều này, bạn có thể xâu chuỗi afterCommit
phương pháp vào hoạt động điều phối của mình:
use App\Jobs\ProcessPodcast;
ProcessPodcast::dispatch($podcast)->afterCommit();
Tương tự như vậy, nếu after_commit
tùy chọn cấu hình được đặt thành true
, bạn có thể chỉ ra rằng một công việc cụ thể nên được điều động ngay lập tức mà không cần đợi bất kỳ giao dịch cơ sở dữ liệu mở nào để cam kết:
ProcessPodcast::dispatch($podcast)->beforeCommit();
Chuỗi công việc
Chuỗi công việc cho phép bạn chỉ định danh sách các công việc được xếp hàng đợi sẽ được chạy theo trình tự sau khi công việc chính được thực thi thành công. Nếu một công việc trong chuỗi không thành công, các công việc còn lại sẽ không được chạy. Để thực hiện một chuỗi công việc được xếp hàng đợi, bạn có thể sử dụng chain
phương pháp được cung cấp bởi Bus
mặt tiền. Xe buýt lệnh của Laravel là một thành phần cấp thấp hơn mà điều phối công việc được xếp hàng đợi được xây dựng trên:
use App\Jobs\OptimizePodcast;
use App\Jobs\ProcessPodcast;
use App\Jobs\ReleasePodcast;
use Illuminate\Support\Facades\Bus;
Bus::chain([
new ProcessPodcast,
new OptimizePodcast,
new ReleasePodcast,
])->dispatch();
Ngoài việc xâu chuỗi các phiên bản lớp công việc, bạn cũng có thể đóng chuỗi:
Bus::chain([
new ProcessPodcast,
new OptimizePodcast,
function () {
Podcast::update(...);
},
])->dispatch();
Việc xóa công việc bằng
$this->delete()
phương pháp trong công việc sẽ không ngăn việc xử lý các công việc được xâu chuỗi. Chuỗi sẽ chỉ ngừng thực hiện nếu một công việc trong chuỗi không thành công.
Kết nối chuỗi & hàng đợi
Nếu bạn muốn chỉ định kết nối và hàng đợi sẽ được sử dụng cho các công việc được xâu chuỗi, bạn có thể sử dụng các phương thức onConnection
và onQueue
. Các phương thức này chỉ định kết nối hàng đợi và tên hàng đợi sẽ được sử dụng trừ khi công việc được xếp hàng đợi được chỉ định rõ ràng một kết nối / hàng đợi khác:
Bus::chain([
new ProcessPodcast,
new OptimizePodcast,
new ReleasePodcast,
])->onConnection('redis')->onQueue('podcasts')->dispatch();
Thất bại trong chuỗi
Khi chuỗi các công việc, bạn có thể sử dụng catch
phương thức để chỉ định một bao đóng sẽ được gọi nếu một công việc trong chuỗi không thành công. Lệnh gọi lại đã cho sẽ nhận được Throwable
trường hợp gây ra lỗi:
use Illuminate\Support\Facades\Bus;
use Throwable;
Bus::chain([
new ProcessPodcast,
new OptimizePodcast,
new ReleasePodcast,
])->catch(function (Throwable $e) {
// A job within the chain has failed...
})->dispatch();
Tùy chỉnh hàng đợi & kết nối
Gửi đến một hàng đợi cụ thể
Bằng cách đẩy các công việc đến các hàng đợi khác nhau, bạn có thể "phân loại" các công việc đã xếp hàng của mình và thậm chí ưu tiên số lượng công nhân mà bạn chỉ định cho các hàng đợi khác nhau. Hãy nhớ rằng điều này không đẩy các công việc đến các "kết nối" hàng đợi khác nhau như được xác định bởi tệp cấu hình hàng đợi của bạn, mà chỉ đến các hàng đợi cụ thể trong một kết nối duy nhất. Để chỉ định hàng đợi, hãy sử dụng onQueue
phương pháp khi điều động công việc:
<?php
namespace App\Http\Controllers;
use App\Http\Controllers\Controller;
use App\Jobs\ProcessPodcast;
use App\Models\Podcast;
use Illuminate\Http\Request;
class PodcastController extends Controller
{
/**
* Store a new podcast.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function store(Request $request)
{
$podcast = Podcast::create(...);
// Create podcast...
ProcessPodcast::dispatch($podcast)->onQueue('processing');
}
}
Ngoài ra, bạn có thể chỉ định hàng đợi của công việc bằng cách gọi onQueue
phương thức trong hàm tạo của công việc:
<?php
namespace App\Jobs;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
class ProcessPodcast implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
/**
* Create a new job instance.
*
* @return void
*/
public function __construct()
{
$this->onQueue('processing');
}
}
Cử đến một kết nối cụ thể
Nếu ứng dụng của bạn tương tác với nhiều kết nối hàng đợi, bạn có thể chỉ định kết nối nào để đẩy một công việc đến bằng onConnection
phương pháp:
<?php
namespace App\Http\Controllers;
use App\Http\Controllers\Controller;
use App\Jobs\ProcessPodcast;
use App\Models\Podcast;
use Illuminate\Http\Request;
class PodcastController extends Controller
{
/**
* Store a new podcast.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function store(Request $request)
{
$podcast = Podcast::create(...);
// Create podcast...
ProcessPodcast::dispatch($podcast)->onConnection('sqs');
}
}
Bạn có thể liên kết các phương thức onConnection
và onQueue
với nhau để chỉ định kết nối và hàng đợi cho một công việc:
ProcessPodcast::dispatch($podcast)
->onConnection('sqs')
->onQueue('processing');
Ngoài ra, bạn có thể chỉ định kết nối của công việc bằng cách gọi onConnection
phương thức trong hàm tạo của công việc:
<?php
namespace App\Jobs;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
class ProcessPodcast implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
/**
* Create a new job instance.
*
* @return void
*/
public function __construct()
{
$this->onConnection('sqs');
}
}
Chỉ định số lần cố gắng tối đa / giá trị thời gian chờ
Số nỗ lực tối đa
Nếu một trong những công việc đã xếp hàng của bạn gặp lỗi, bạn có thể không muốn nó tiếp tục thử lại vô thời hạn. Do đó, Laravel cung cấp nhiều cách khác nhau để chỉ định số lần hoặc bao lâu một công việc có thể được thực hiện.
Một cách tiếp cận để chỉ định số lần tối đa một công việc có thể được thử là thông qua công --tries
tắc trên dòng lệnh Artisan. Điều này sẽ áp dụng cho tất cả các công việc được xử lý bởi nhân viên trừ khi công việc đang được xử lý chỉ định một số lần cụ thể hơn mà nó có thể được thực hiện:
php artisan queue:work --tries=3
Nếu một công việc vượt quá số lần thử tối đa của nó, nó sẽ được coi là một công việc "thất bại". Để biết thêm thông tin về cách xử lý công việc không thành công, hãy tham khảo tài liệu công việc không thành công .
Bạn có thể thực hiện một cách tiếp cận chi tiết hơn bằng cách xác định số lần tối đa một công việc có thể được thực hiện trên chính loại công việc đó. Nếu số lần thử tối đa được chỉ định cho công việc, nó sẽ được ưu tiên hơn --tries
giá trị được cung cấp trên dòng lệnh:
<?php
namespace App\Jobs;
class ProcessPodcast implements ShouldQueue
{
/**
* The number of times the job may be attempted.
*
* @var int
*/
public $tries = 5;
}
Nỗ lực dựa trên thời gian
Để thay thế cho việc xác định số lần một công việc có thể được thử trước khi nó thất bại, bạn có thể xác định thời điểm mà công việc đó không nên được thực hiện nữa. Điều này cho phép một công việc được thực hiện bất kỳ số lần nào trong một khung thời gian nhất định. Để xác định thời gian mà một công việc không nên được thực hiện nữa, hãy thêm một retryUntil
phương thức vào lớp công việc của bạn. Phương thức này sẽ trả về một DateTime
phiên bản:
/**
* Determine the time at which the job should timeout.
*
* @return \DateTime
*/
public function retryUntil()
{
return now()->addMinutes(10);
}
Bạn cũng có thể xác định một thuộc
tries
tính hoặcretryUntil
phương thức trên trình lắng nghe sự kiện được xếp hàng đợi của bạn .
Các trường hợp ngoại lệ tối đa
Đôi khi bạn có thể muốn chỉ định rằng một công việc có thể được thử nhiều lần, nhưng sẽ thất bại nếu các lần thử lại được kích hoạt bởi một số ngoại lệ chưa được xử lý nhất định (trái ngược với việc được phát hành release
trực tiếp bằng phương pháp). Để thực hiện điều này, bạn có thể xác định một thuộc maxExceptions
tính trên lớp công việc của mình:
<?php
namespace App\Jobs;
use Illuminate\Support\Facades\Redis;
class ProcessPodcast implements ShouldQueue
{
/**
* The number of times the job may be attempted.
*
* @var int
*/
public $tries = 25;
/**
* The maximum number of unhandled exceptions to allow before failing.
*
* @var int
*/
public $maxExceptions = 3;
/**
* Execute the job.
*
* @return void
*/
public function handle()
{
Redis::throttle('key')->allow(10)->every(60)->then(function () {
// Lock obtained, process the podcast...
}, function () {
// Unable to obtain lock...
return $this->release(10);
});
}
}
Trong ví dụ này, lệnh được giải phóng trong mười giây nếu ứng dụng không thể lấy được Redis lock và sẽ tiếp tục được thử lại tối đa 25 lần. Tuy nhiên, công việc sẽ thất bại nếu công việc ném ra ba trường hợp ngoại lệ chưa được khắc phục.
Hết giờ
Phần
pcntl
mở rộng PHP phải được cài đặt để chỉ định thời gian chờ công việc.
Thông thường, bạn biết đại khái bạn mong đợi các công việc xếp hàng đợi của mình mất bao lâu. Vì lý do này, Laravel cho phép bạn chỉ định giá trị "thời gian chờ". Nếu một công việc đang xử lý lâu hơn số giây được chỉ định bởi giá trị thời gian chờ, công nhân đang xử lý công việc sẽ thoát ra với một lỗi. Thông thường, công nhân sẽ được khởi động lại tự động bởi trình quản lý quy trình được định cấu hình trên máy chủ của bạn .
Số giây tối đa mà lệnh có thể chạy có thể được chỉ định bằng cách sử dụng công --timeout
tắc trên dòng lệnh Artisan:
php artisan queue:work --timeout=30
Nếu công việc vượt quá số lần cố gắng tối đa của nó do liên tục hết thời gian, nó sẽ được đánh dấu là không thành công.
Bạn cũng có thể xác định số giây tối đa mà một công việc được phép chạy trên chính lớp công việc đó. Nếu thời gian chờ được chỉ định trên công việc, nó sẽ được ưu tiên hơn bất kỳ thời gian chờ nào được chỉ định trên dòng lệnh:
<?php
namespace App\Jobs;
class ProcessPodcast implements ShouldQueue
{
/**
* The number of seconds the job can run before timing out.
*
* @var int
*/
public $timeout = 120;
}
Đôi khi, các quy trình chặn IO như ổ cắm hoặc kết nối HTTP gửi đi có thể không tôn trọng thời gian chờ đã chỉ định của bạn. Do đó, khi sử dụng các tính năng này, bạn cũng nên cố gắng chỉ định thời gian chờ bằng cách sử dụng các API của chúng. Ví dụ: khi sử dụng Guzzle, bạn phải luôn chỉ định kết nối và yêu cầu giá trị thời gian chờ.
Không đúng thời gian chờ
Nếu bạn muốn chỉ ra rằng một công việc phải được đánh dấu là không thành công khi hết thời gian, bạn có thể xác định thuộc $failOnTimeout
tính trên loại công việc:
/**
* Indicate if the job should be marked as failed on timeout.
*
* @var bool
*/
public $failOnTimeout = true;
Xử lý lỗi
Nếu một ngoại lệ được đưa ra trong khi công việc đang được xử lý, công việc sẽ tự động được đưa trở lại hàng đợi để nó có thể được thử lại. Công việc sẽ tiếp tục được phát hành cho đến khi nó đã được thử số lần tối đa mà ứng dụng của bạn cho phép. Số lần thử tối đa được xác định bằng công --tries
tắc được sử dụng trên queue:work
lệnh Artisan. Ngoài ra, số lần thử tối đa có thể được xác định trên chính lớp công việc. Bạn có thể tìm thêm thông tin về cách chạy nhân viên xếp hàng bên dưới .
Hoàn thành công việc theo cách thủ công
Đôi khi bạn có thể muốn đưa một công việc trở lại hàng đợi theo cách thủ công để có thể thử lại công việc sau đó. Bạn có thể thực hiện điều này bằng cách gọi release
phương thức:
/**
* Execute the job.
*
* @return void
*/
public function handle()
{
// ...
$this->release();
}
Theo mặc định, release
phương thức sẽ giải phóng công việc trở lại hàng đợi để xử lý ngay lập tức. Tuy nhiên, bằng cách chuyển một số nguyên cho release
phương thức, bạn có thể hướng dẫn hàng đợi không cung cấp công việc để xử lý cho đến khi trôi qua một số giây nhất định:
$this->release(10);
Thất bại một cách thủ công một công việc
Đôi khi, bạn có thể cần phải tự đánh dấu công việc là "không thành công". Để làm như vậy, bạn có thể gọi fail
phương thức:
/**
* Execute the job.
*
* @return void
*/
public function handle()
{
// ...
$this->fail();
}
Nếu bạn muốn đánh dấu công việc của mình là không thành công vì một ngoại lệ mà bạn đã mắc phải, bạn có thể chuyển ngoại lệ cho fail
phương thức:
$this->fail($exception);
Để biết thêm thông tin về các công việc thất bại, hãy xem tài liệu về cách xử lý các công việc thất bại .
Công việc theo lô
Tính năng chia lô công việc của Laravel cho phép bạn dễ dàng thực hiện một loạt công việc và sau đó thực hiện một số hành động khi lô công việc đã hoàn thành việc thực thi. Trước khi bắt đầu, bạn nên tạo một quá trình di chuyển cơ sở dữ liệu để xây dựng một bảng chứa thông tin meta về các lô công việc của bạn, chẳng hạn như tỷ lệ phần trăm hoàn thành của chúng. Quá trình di chuyển này có thể được tạo bằng queue:batches-table
lệnh Artisan:
php artisan queue:batches-table
php artisan migrate
Xác định công việc có thể làm được
Để xác định một công việc có thể thực hiện được, bạn nên tạo một công việc có thể xếp hàng như bình thường; tuy nhiên, bạn nên thêm Illuminate\Bus\Batchable
đặc điểm vào lớp công việc. Đặc điểm này cung cấp quyền truy cập vào một batch
phương thức có thể được sử dụng để truy xuất lô hiện tại mà công việc đang thực hiện bên trong:
<?php
namespace App\Jobs;
use Illuminate\Bus\Batchable;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
class ImportCsv implements ShouldQueue
{
use Batchable, Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
/**
* Execute the job.
*
* @return void
*/
public function handle()
{
if ($this->batch()->cancelled()) {
// Determine if the batch has been cancelled...
return;
}
// Import a portion of the CSV file...
}
}
Gửi hàng loạt
Để điều động một loạt công việc, bạn nên sử dụng batch
phương pháp của Bus
mặt tiền. Tất nhiên, theo lô chủ yếu hữu ích khi được kết hợp với các lệnh gọi lại hoàn thành. Vì vậy, bạn có thể sử dụng then
, catch
và finally
phương pháp để xác định callbacks hoàn cho hàng loạt. Mỗi lệnh gọi lại này sẽ nhận được một Illuminate\Bus\Batch
thể hiện khi chúng được gọi. Trong ví dụ này, chúng ta sẽ tưởng tượng chúng ta đang xếp hàng một loạt công việc mà mỗi công việc xử lý một số hàng nhất định từ tệp CSV:
use App\Jobs\ImportCsv;
use Illuminate\Bus\Batch;
use Illuminate\Support\Facades\Bus;
use Throwable;
$batch = Bus::batch([
new ImportCsv(1, 100),
new ImportCsv(101, 200),
new ImportCsv(201, 300),
new ImportCsv(301, 400),
new ImportCsv(401, 500),
])->then(function (Batch $batch) {
// All jobs completed successfully...
})->catch(function (Batch $batch, Throwable $e) {
// First batch job failure detected...
})->finally(function (Batch $batch) {
// The batch has finished executing...
})->dispatch();
return $batch->id;
ID của lô, có thể được truy cập thông qua thuộc $batch->id
tính, có thể được sử dụng để truy vấn bus lệnh Laravel để biết thông tin về lô sau khi nó đã được gửi đi.
Đặt tên hàng loạt
Một số công cụ như Laravel Horizon và Laravel Telescope có thể cung cấp thông tin gỡ lỗi thân thiện với người dùng hơn cho các lô nếu các lô được đặt tên. Để gán một tên tùy ý cho một lô, bạn có thể gọi name
phương thức trong khi xác định lô:
$batch = Bus::batch([
// ...
])->then(function (Batch $batch) {
// All jobs completed successfully...
})->name('Import CSV')->dispatch();
Kết nối hàng loạt & hàng đợi
Nếu bạn muốn chỉ định kết nối và hàng đợi sẽ được sử dụng cho các công việc theo đợt, bạn có thể sử dụng các phương thức onConnection
và onQueue
. Tất cả các công việc theo đợt phải thực thi trong cùng một kết nối và hàng đợi:
$batch = Bus::batch([
// ...
])->then(function (Batch $batch) {
// All jobs completed successfully...
})->onConnection('redis')->onQueue('imports')->dispatch();
Chuỗi trong lô
Bạn có thể xác định một tập hợp các công việc được xâu chuỗi trong một lô bằng cách đặt các công việc được xâu chuỗi trong một mảng. Ví dụ: chúng tôi có thể thực hiện song song hai chuỗi công việc và thực hiện một lệnh gọi lại khi cả hai chuỗi công việc đã hoàn tất xử lý:
use App\Jobs\ReleasePodcast;
use App\Jobs\SendPodcastReleaseNotification;
use Illuminate\Bus\Batch;
use Illuminate\Support\Facades\Bus;
Bus::batch([
[
new ReleasePodcast(1),
new SendPodcastReleaseNotification(1),
],
[
new ReleasePodcast(2),
new SendPodcastReleaseNotification(2),
],
])->then(function (Batch $batch) {
// ...
})->dispatch();
Thêm công việc vào hàng loạt
Đôi khi, có thể hữu ích khi thêm các công việc bổ sung vào một lô từ bên trong một công việc theo lô. Mẫu này có thể hữu ích khi bạn cần hàng nghìn công việc có thể mất quá nhiều thời gian để gửi đi trong một yêu cầu web. Vì vậy, thay vào đó, bạn có thể muốn gửi một loạt công việc "trình tải" ban đầu để hydrat hóa lô với nhiều công việc hơn:
$batch = Bus::batch([
new LoadImportBatch,
new LoadImportBatch,
new LoadImportBatch,
])->then(function (Batch $batch) {
// All jobs completed successfully...
})->name('Import Contacts')->dispatch();
Trong ví dụ này, chúng ta sẽ sử dụng LoadImportBatch
công việc để hydrat hóa lô với các công việc bổ sung. Để thực hiện điều này, chúng tôi có thể sử dụng add
phương thức trên phiên bản hàng loạt có thể được truy cập thông qua batch
phương thức của công việc :
use App\Jobs\ImportContacts;
use Illuminate\Support\Collection;
/**
* Execute the job.
*
* @return void
*/
public function handle()
{
if ($this->batch()->cancelled()) {
return;
}
$this->batch()->add(Collection::times(1000, function () {
return new ImportContacts;
}));
}
Bạn chỉ có thể thêm công việc vào một lô từ bên trong một công việc thuộc cùng một lô.
Kiểm tra hàng loạt
Phiên bản Illuminate\Bus\Batch
được cung cấp cho các lệnh gọi lại hoàn thành hàng loạt có nhiều thuộc tính và phương pháp để hỗ trợ bạn trong việc tương tác và kiểm tra một loạt công việc nhất định:
// The UUID of the batch...
$batch->id;
// The name of the batch (if applicable)...
$batch->name;
// The number of jobs assigned to the batch...
$batch->totalJobs;
// The number of jobs that have not been processed by the queue...
$batch->pendingJobs;
// The number of jobs that have failed...
$batch->failedJobs;
// The number of jobs that have been processed thus far...
$batch->processedJobs();
// The completion percentage of the batch (0-100)...
$batch->progress();
// Indicates if the batch has finished executing...
$batch->finished();
// Cancel the execution of the batch...
$batch->cancel();
// Indicates if the batch has been cancelled...
$batch->cancelled();
Trả lại hàng loạt từ các tuyến
Tất cả các Illuminate\Bus\Batch
phiên bản đều có thể tuần tự hóa JSON, có nghĩa là bạn có thể trả lại chúng trực tiếp từ một trong các tuyến ứng dụng của mình để truy xuất tải trọng JSON chứa thông tin về lô, bao gồm cả tiến độ hoàn thành của nó. Điều này giúp thuận tiện khi hiển thị thông tin về tiến độ hoàn thành của lô trong giao diện người dùng của ứng dụng của bạn.
Để truy xuất một lô theo ID của nó, bạn có thể sử dụng phương pháp Bus
của mặt tiền findBatch
:
use Illuminate\Support\Facades\Bus;
use Illuminate\Support\Facades\Route;
Route::get('/batch/{batchId}', function (string $batchId) {
return Bus::findBatch($batchId);
});
Hủy hàng loạt
Đôi khi bạn có thể cần phải hủy thực hiện một lô nhất định. Điều này có thể được thực hiện bằng cách gọi cancel
phương thức trên Illuminate\Bus\Batch
phiên bản:
/**
* Execute the job.
*
* @return void
*/
public function handle()
{
if ($this->user->exceedsImportLimit()) {
return $this->batch()->cancel();
}
if ($this->batch()->cancelled()) {
return;
}
}
Như bạn có thể đã nhận thấy trong các ví dụ trước, các công việc theo lô thường nên kiểm tra xem liệu lô đã bị hủy ở đầu handle
phương pháp của chúng hay chưa :
/**
* Execute the job.
*
* @return void
*/
public function handle()
{
if ($this->batch()->cancelled()) {
return;
}
// Continue processing...
}
Lỗi hàng loạt
Khi một công việc theo đợt không thành công, lệnh catch
gọi lại (nếu được chỉ định) sẽ được gọi. Lệnh gọi lại này chỉ được gọi cho công việc đầu tiên không thành công trong lô.
Cho phép thất bại
Khi một công việc trong một lô không thành công, Laravel sẽ tự động đánh dấu lô là "bị hủy". Nếu bạn muốn, bạn có thể vô hiệu hóa hành vi này để lỗi công việc không tự động đánh dấu lô là đã bị hủy. Điều này có thể được thực hiện bằng cách gọi allowFailures
phương thức trong khi gửi lô:
$batch = Bus::batch([
// ...
])->then(function (Batch $batch) {
// All jobs completed successfully...
})->allowFailures()->dispatch();
Thử lại Hàng loạt Công việc Không thành công
Để thuận tiện, Laravel cung cấp queue:retry-batch
lệnh Artisan cho phép bạn dễ dàng thử lại tất cả các công việc bị lỗi cho một lô nhất định. Các queue:retry-batch
lệnh chấp nhận UUID của hàng loạt mà công việc thất bại nên được thử lại:
php artisan queue:retry-batch 32dbc76c-4f82-4749-b610-a639fe0099b5
Cắt tỉa hàng loạt
Nếu không cắt tỉa, job_batches
bảng có thể tích lũy hồ sơ rất nhanh chóng. Để hạn chế điều này, bạn nên sắp xếp các queue:prune-batches
lệnh Artisan để chạy hàng ngày:
$schedule->command('queue:prune-batches')->daily();
Theo mặc định, tất cả các lô thành phẩm tồn tại hơn 24 giờ sẽ bị cắt bỏ. Bạn có thể sử dụng hours
tùy chọn khi gọi lệnh để xác định thời gian lưu giữ dữ liệu hàng loạt. Ví dụ: lệnh sau sẽ xóa tất cả các lô đã hoàn thành hơn 48 giờ trước:
$schedule->command('queue:prune-batches --hours=48')->daily();
Đôi khi, jobs_batches
bảng của bạn có thể tích lũy các bản ghi lô cho các lô không bao giờ hoàn thành thành công, chẳng hạn như các lô trong đó một công việc không thành công và công việc đó không bao giờ được thử lại thành công. Bạn có thể hướng dẫn queue:prune-batches
lệnh để loại bỏ các bản ghi lô chưa hoàn thành này bằng cách sử dụng unfinished
tùy chọn:
$schedule->command('queue:prune-batches --hours=48 --unfinished=72')->daily();
Xếp hàng đóng cửa
Thay vì điều động một lớp công việc vào hàng đợi, bạn cũng có thể gửi một kết thúc. Điều này rất tốt cho các tác vụ nhanh chóng, đơn giản cần được thực hiện bên ngoài chu kỳ yêu cầu hiện tại. Khi gửi các đóng đến hàng đợi, nội dung mã của đóng được ký bằng mật mã để không thể sửa đổi khi chuyển tiếp:
$podcast = App\Podcast::find(1);
dispatch(function () use ($podcast) {
$podcast->publish();
});
Sử dụng catch
phương pháp này, bạn có thể cung cấp một đóng sẽ được thực thi nếu quá trình đóng trong hàng đợi không hoàn thành thành công sau khi hoàn thành tất cả các lần thử lại đã định cấu hình của hàng đợi của bạn :
use Throwable;
dispatch(function () use ($podcast) {
$podcast->publish();
})->catch(function (Throwable $e) {
// This job has failed...
});
Chạy Công nhân Hàng đợi
các queue:work
lệnh
Laravel bao gồm một lệnh Artisan sẽ bắt đầu một nhân viên hàng đợi và xử lý các công việc mới khi chúng được đẩy vào hàng đợi. Bạn có thể chạy công nhân bằng queue:work
lệnh Artisan. Lưu ý rằng khi queue:work
lệnh đã bắt đầu, nó sẽ tiếp tục chạy cho đến khi dừng theo cách thủ công hoặc bạn đóng thiết bị đầu cuối của mình:
php artisan queue:work
Để giữ cho
queue:work
quá trình chạy vĩnh viễn trong nền, bạn nên sử dụng một trình giám sát quá trình như Người giám sát để đảm bảo rằng nhân viên hàng đợi không ngừng chạy.
Hãy nhớ rằng, công nhân hàng đợi, là các quy trình tồn tại lâu dài và lưu trữ trạng thái ứng dụng đã khởi động trong bộ nhớ. Do đó, họ sẽ không nhận thấy những thay đổi trong cơ sở mã của bạn sau khi chúng đã được khởi động. Vì vậy, trong quá trình triển khai của bạn, hãy đảm bảo khởi động lại nhân viên hàng đợi của bạn . Ngoài ra, hãy nhớ rằng bất kỳ trạng thái tĩnh nào được tạo hoặc sửa đổi bởi ứng dụng của bạn sẽ không được tự động đặt lại giữa các công việc.
Ngoài ra, bạn có thể chạy queue:listen
lệnh. Khi sử dụng queue:listen
lệnh, bạn không phải khởi động lại nhân viên theo cách thủ công khi bạn muốn tải lại mã đã cập nhật của mình hoặc đặt lại trạng thái ứng dụng; tuy nhiên, lệnh này kém hiệu quả hơn đáng kể so với queue:work
lệnh:
php artisan queue:listen
Chạy nhiều nhân viên xếp hàng
Để chỉ định nhiều công nhân vào một hàng đợi và xử lý công việc đồng thời, bạn chỉ cần bắt đầu nhiều queue:work
quy trình. Điều này có thể được thực hiện cục bộ thông qua nhiều tab trong thiết bị đầu cuối của bạn hoặc trong quá trình sản xuất bằng cách sử dụng cài đặt cấu hình của trình quản lý quy trình của bạn. Khi sử dụng Supervisor , bạn có thể sử dụng numprocs
giá trị cấu hình.
Chỉ định Kết nối & Hàng đợi
Bạn cũng có thể chỉ định kết nối hàng đợi mà nhân viên sẽ sử dụng. Tên kết nối được chuyển đến work
lệnh phải tương ứng với một trong các kết nối được xác định trong config/queue.php
tệp cấu hình của bạn :
php artisan queue:work redis
Bạn có thể tùy chỉnh nhân viên hàng đợi của mình hơn nữa bằng cách chỉ xử lý các hàng đợi cụ thể cho một kết nối nhất định. Ví dụ: nếu tất cả các email của bạn được xử lý trong một emails
hàng đợi trên redis
kết nối hàng đợi của bạn , bạn có thể ra lệnh sau để bắt đầu một nhân viên chỉ xử lý hàng đợi đó:
php artisan queue:work redis --queue=emails
Xử lý một số lượng công việc cụ thể
Các --once
tùy chọn có thể được sử dụng để hướng dẫn người lao động để chỉ xử lý một công việc duy nhất từ hàng đợi:
php artisan queue:work --once
Các --max-jobs
tùy chọn có thể được sử dụng để hướng dẫn người lao động để xử lý số lượng nhất định của việc làm và lối ra sau đó. Tùy chọn này có thể hữu ích khi được kết hợp với Người giám sát để công nhân của bạn tự động khởi động lại sau khi xử lý một số công việc nhất định, giải phóng bất kỳ bộ nhớ nào mà họ có thể đã tích lũy:
php artisan queue:work --max-jobs=1000
Xử lý tất cả công việc đã xếp hàng và sau đó thoát
Các --stop-when-empty
tùy chọn có thể được sử dụng để hướng dẫn người lao động để xử lý tất cả các công ăn việc làm và sau đó kết thúc tốt đẹp. Tùy chọn này có thể hữu ích khi xử lý hàng đợi Laravel trong vùng chứa Docker nếu bạn muốn tắt vùng chứa sau khi hàng đợi trống:
php artisan queue:work --stop-when-empty
Xử lý công việc trong một số giây nhất định
Các --max-time
tùy chọn có thể được sử dụng để hướng dẫn người lao động làm công việc xử lý đối với số lượng nhất định của giây và thoát sau đó. Tùy chọn này có thể hữu ích khi được kết hợp với Người giám sát để công nhân của bạn tự động khởi động lại sau khi xử lý công việc trong một khoảng thời gian nhất định, giải phóng bất kỳ bộ nhớ nào mà họ có thể đã tích lũy:
// Process jobs for one hour and then exit...
php artisan queue:work --max-time=3600
Thời lượng ngủ của người lao động
Khi các công việc có sẵn trên hàng đợi, công nhân sẽ tiếp tục xử lý các công việc mà không có sự chậm trễ giữa chúng. Tuy nhiên, sleep
tùy chọn xác định công nhân sẽ "ngủ" trong bao nhiêu giây nếu không có công việc mới. Trong khi ngủ, công nhân sẽ không xử lý bất kỳ công việc mới nào - các công việc sẽ được xử lý sau khi công nhân thức dậy trở lại.
php artisan queue:work --sleep=3
Cân nhắc tài nguyên
Daemon queue worker không "khởi động lại" khuôn khổ trước khi xử lý mỗi công việc. Do đó, bạn nên giải phóng bất kỳ tài nguyên nặng nào sau khi mỗi công việc hoàn thành. Ví dụ: nếu bạn đang thực hiện thao tác hình ảnh với thư viện GD, bạn nên giải phóng bộ nhớ imagedestroy
khi bạn xử lý xong hình ảnh.
Ưu tiên hàng đợi
Đôi khi bạn có thể muốn ưu tiên cách hàng đợi của mình được xử lý. Ví dụ: trong config/queue.php
tệp cấu hình của bạn , bạn có thể đặt mặc định queue
cho redis
kết nối của mình thành low
. Tuy nhiên, đôi khi bạn có thể muốn đẩy một công việc lên high
hàng đợi ưu tiên như vậy:
dispatch((new Job)->onQueue('high'));
Để bắt đầu một worker xác minh rằng tất cả các high
công việc hàng đợi đã được xử lý trước khi tiếp tục bất kỳ công việc nào trên low
hàng đợi, hãy chuyển một danh sách tên hàng đợi được phân tách bằng dấu phẩy vào work
lệnh:
php artisan queue:work --queue=high,low
Nhân viên xếp hàng & Triển khai
Vì công nhân hàng đợi là các quy trình tồn tại lâu dài, họ sẽ không nhận thấy các thay đổi đối với mã của bạn nếu không được khởi động lại. Vì vậy, cách đơn giản nhất để triển khai một ứng dụng sử dụng công nhân hàng đợi là khởi động lại công nhân trong quá trình triển khai của bạn. Bạn có thể khởi động lại tất cả các công nhân một cách duyên dáng bằng cách ra queue:restart
lệnh:
php artisan queue:restart
Lệnh này sẽ hướng dẫn tất cả nhân viên hàng đợi thoát một cách duyên dáng sau khi họ xử lý xong công việc hiện tại để không có công việc hiện có nào bị mất. Vì nhân viên hàng đợi sẽ thoát khi queue:restart
lệnh được thực thi, bạn nên chạy trình quản lý quy trình như Người giám sát để tự động khởi động lại nhân viên hàng đợi.
Hàng đợi sử dụng bộ nhớ cache để lưu trữ các tín hiệu khởi động lại, vì vậy bạn nên xác minh rằng trình điều khiển bộ nhớ cache được định cấu hình đúng cho ứng dụng của bạn trước khi sử dụng tính năng này.
Hết hạn công việc & hết thời gian
Hết hạn công việc
Trong config/queue.php
tệp cấu hình của bạn , mỗi kết nối hàng đợi xác định một retry_after
tùy chọn. Tùy chọn này chỉ định kết nối hàng đợi sẽ đợi bao nhiêu giây trước khi thử lại một công việc đang được xử lý. Ví dụ: nếu giá trị của retry_after
được đặt thành 90
, công việc sẽ được giải phóng trở lại hàng đợi nếu nó đã được xử lý trong 90 giây mà không bị giải phóng hoặc xóa. Thông thường, bạn nên đặt retry_after
giá trị thành số giây tối đa mà công việc của bạn phải thực hiện một cách hợp lý để hoàn tất quá trình xử lý.
Kết nối hàng đợi duy nhất không chứa
retry_after
giá trị là Amazon SQS. SQS sẽ thử lại công việc dựa trên Thời gian chờ hiển thị mặc định được quản lý trong bảng điều khiển AWS.
Thời gian chờ của công nhân
Lệnh queue:work
Artisan hiển thị một --timeout
tùy chọn. Nếu một công việc đang xử lý lâu hơn số giây được chỉ định bởi giá trị thời gian chờ, công nhân đang xử lý công việc sẽ thoát ra với một lỗi. Thông thường, công nhân sẽ được khởi động lại tự động bởi trình quản lý quy trình được định cấu hình trên máy chủ của bạn :
php artisan queue:work --timeout=60
Các retry_after
tùy chọn cấu hình và các --timeout
tùy chọn CLI là khác nhau, nhưng làm việc cùng nhau để đảm bảo công việc mà không bị mất và các công việc mà chỉ xử lý thành công một lần.
Các
--timeout
giá trị nên luôn luôn có ít nhất là vài giây ngắn hơn của bạnretry_after
giá trị cấu hình. Điều này sẽ đảm bảo rằng một công nhân đang xử lý công việc bị đóng băng luôn được kết thúc trước khi công việc được thử lại. Nếu--timeout
tùy chọn của bạn dài hơnretry_after
giá trị cấu hình, công việc của bạn có thể được xử lý hai lần.
Cấu hình người giám sát
Trong quá trình sản xuất, bạn cần một cách để giữ cho các queue:work
quy trình của mình hoạt động. Một queue:work
quá trình có thể ngừng chạy cho một loạt các lý do, chẳng hạn như một thời gian chờ người lao động vượt quá hoặc thực hiện các queue:restart
lệnh.
Vì lý do này, bạn cần định cấu hình một trình giám sát quy trình có thể phát hiện khi nào các queue:work
quy trình của bạn thoát và tự động khởi động lại chúng. Ngoài ra, trình theo dõi quy trình có thể cho phép bạn chỉ định số lượng queue:work
quy trình bạn muốn chạy đồng thời. Supervisor là một trình giám sát quá trình thường được sử dụng trong môi trường Linux và chúng ta sẽ thảo luận về cách cấu hình nó trong tài liệu sau.
Cài đặt trình giám sát
Người giám sát là một trình giám sát quá trình cho hệ điều hành Linux và sẽ tự động khởi động lại các queue:work
quy trình của bạn nếu chúng không thành công. Để cài đặt Supervisor trên Ubuntu, bạn có thể sử dụng lệnh sau:
sudo apt-get install supervisor
Nếu việc tự cấu hình và quản lý Supervisor nghe có vẻ quá sức, hãy cân nhắc sử dụng Laravel Forge , ứng dụng này sẽ tự động cài đặt và cấu hình Supervisor cho các dự án Laravel sản xuất của bạn.
Định cấu hình người giám sát
Các tệp cấu hình người giám sát thường được lưu trữ trong /etc/supervisor/conf.d
thư mục. Trong thư mục này, bạn có thể tạo bất kỳ số lượng tệp cấu hình nào hướng dẫn người giám sát cách giám sát các quy trình của bạn. Ví dụ: hãy tạo một laravel-worker.conf
tệp khởi động và giám sát queue:work
các quy trình:
[program:laravel-worker]
process_name=%(program_name)s_%(process_num)02d
command=php /home/forge/app.com/artisan queue:work sqs --sleep=3 --tries=3 --max-time=3600
autostart=true
autorestart=true
stopasgroup=true
killasgroup=true
user=forge
numprocs=8
redirect_stderr=true
stdout_logfile=/home/forge/app.com/worker.log
stopwaitsecs=3600
Trong ví dụ này, numprocs
chỉ thị sẽ hướng dẫn Người giám sát chạy tám queue:work
quy trình và giám sát tất cả chúng, tự động khởi động lại chúng nếu chúng không thành công. Bạn nên thay đổi command
chỉ thị của cấu hình để phản ánh kết nối hàng đợi mong muốn của bạn và các tùy chọn công nhân.
Bạn nên đảm bảo rằng giá trị của
stopwaitsecs
lớn hơn số giây được sử dụng bởi công việc đang chạy lâu nhất của bạn. Nếu không, Người giám sát có thể giết công việc trước khi nó được xử lý xong.
Giám sát bắt đầu
Khi tệp cấu hình đã được tạo, bạn có thể cập nhật cấu hình Người giám sát và bắt đầu các quy trình bằng các lệnh sau:
sudo supervisorctl reread
sudo supervisorctl update
sudo supervisorctl start laravel-worker:*
Để biết thêm thông tin về Người giám sát, hãy tham khảo tài liệu Người giám sát .
Xử lý công việc thất bại
Đôi khi công việc xếp hàng của bạn sẽ thất bại. Đừng lo lắng, mọi thứ không phải lúc nào cũng diễn ra theo kế hoạch! Laravel bao gồm một cách thuận tiện để chỉ định số lần tối đa một công việc nên được thực hiện . Sau khi một công việc vượt quá số lần thử này, nó sẽ được chèn vào failed_jobs
bảng cơ sở dữ liệu. Tất nhiên, chúng ta sẽ cần tạo bảng đó nếu nó chưa tồn tại. Để tạo sự di chuyển cho failed_jobs
bảng, bạn có thể sử dụng queue:failed-table
lệnh:
php artisan queue:failed-table
php artisan migrate
Khi chạy quy trình công nhân hàng đợi , bạn có thể chỉ định số lần tối đa một công việc nên được thực hiện bằng cách sử dụng công --tries
tắc trên queue:work
lệnh. Nếu bạn không chỉ định giá trị cho --tries
tùy chọn, các công việc sẽ chỉ được thực hiện một lần hoặc nhiều lần như được chỉ định bởi thuộc tính của lớp công việc $tries
:
php artisan queue:work redis --tries=3
Sử dụng --backoff
tùy chọn này, bạn có thể chỉ định Laravel sẽ đợi bao nhiêu giây trước khi thử lại một công việc gặp phải ngoại lệ. Theo mặc định, một công việc ngay lập tức được đưa trở lại hàng đợi để nó có thể được thử lại:
php artisan queue:work redis --tries=3 --backoff=3
Nếu bạn muốn định cấu hình Laravel sẽ đợi bao nhiêu giây trước khi thử lại một công việc gặp phải ngoại lệ trên cơ sở từng công việc, bạn có thể thực hiện việc này bằng cách xác định một thuộc backoff
tính trên lớp công việc của mình:
/**
* The number of seconds to wait before retrying the job.
*
* @var int
*/
public $backoff = 3;
Nếu bạn yêu cầu logic phức tạp hơn để xác định thời gian chờ của công việc, bạn có thể xác định một backoff
phương thức trên lớp công việc của mình:
/**
* Calculate the number of seconds to wait before retrying the job.
*
* @return int
*/
public function backoff()
{
return 3;
}
Bạn có thể dễ dàng định cấu hình dự phòng "theo cấp số nhân" bằng cách trả về một mảng các giá trị dự phòng từ backoff
phương thức. Trong ví dụ này, độ trễ khi thử lại sẽ là 1 giây cho lần thử lại đầu tiên, 5 giây cho lần thử lại thứ hai và 10 giây cho lần thử lại thứ ba:
/**
* Calculate the number of seconds to wait before retrying the job.
*
* @return array
*/
public function backoff()
{
return [1, 5, 10];
}
Dọn dẹp sau khi công việc thất bại
Khi một công việc cụ thể không thành công, bạn có thể muốn gửi cảnh báo cho người dùng của mình hoặc hoàn nguyên bất kỳ hành động nào đã được hoàn thành một phần bởi công việc. Để thực hiện điều này, bạn có thể xác định một failed
phương thức trên lớp công việc của mình. Phiên bản Throwable
khiến công việc không thành công sẽ được chuyển cho failed
phương thức:
<?php
namespace App\Jobs;
use App\Models\Podcast;
use App\Services\AudioProcessor;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Throwable;
class ProcessPodcast implements ShouldQueue
{
use InteractsWithQueue, Queueable, SerializesModels;
/**
* The podcast instance.
*
* @var \App\Podcast
*/
protected $podcast;
/**
* Create a new job instance.
*
* @param \App\Models\Podcast $podcast
* @return void
*/
public function __construct(Podcast $podcast)
{
$this->podcast = $podcast;
}
/**
* Execute the job.
*
* @param \App\Services\AudioProcessor $processor
* @return void
*/
public function handle(AudioProcessor $processor)
{
// Process uploaded podcast...
}
/**
* Handle a job failure.
*
* @param \Throwable $exception
* @return void
*/
public function failed(Throwable $exception)
{
// Send user notification of failure, etc...
}
}
Một phiên bản mới của công việc được khởi tạo trước khi gọi
failed
phương thức; do đó, bất kỳ sửa đổi thuộc tính lớp nào có thể đã xảy ra tronghandle
phương thức sẽ bị mất.
Thử lại Công việc Không thành công
Để xem tất cả các công việc không thành công đã được chèn vào failed_jobs
bảng cơ sở dữ liệu của bạn , bạn có thể sử dụng queue:failed
lệnh Artisan:
php artisan queue:failed
Các queue:failed
lệnh sẽ liệt kê các ID công việc, kết nối, hàng đợi, thời gian thất bại, và các thông tin khác về công việc. ID công việc có thể được sử dụng để thử lại công việc không thành công. Ví dụ: để thử lại một công việc không thành công có ID là 5
, hãy sử dụng lệnh sau:
php artisan queue:retry 5
Nếu cần, bạn có thể chuyển nhiều ID hoặc một dải ID (khi sử dụng ID số) vào lệnh:
php artisan queue:retry 5 6 7 8 9 10
php artisan queue:retry --range=5-10
Bạn cũng có thể thử lại tất cả các công việc không thành công cho một hàng đợi cụ thể:
php artisan queue:retry --queue=name
Để thử lại tất cả các công việc không thành công của bạn, hãy thực hiện queue:retry
lệnh và chuyển all
làm ID:
php artisan queue:retry all
Nếu bạn muốn xóa công việc không thành công, bạn có thể sử dụng queue:forget
lệnh:
php artisan queue:forget 5
Khi sử dụng Horizon , bạn nên sử dụng
horizon:forget
lệnh xóa công việc bị lỗi thay vìqueue:forget
lệnh.
Để xóa tất cả các công việc không thành công của bạn khỏi failed_jobs
bảng, bạn có thể sử dụng queue:flush
lệnh:
php artisan queue:flush
Bỏ qua các mô hình bị thiếu
Khi đưa một mô hình Eloquent vào một công việc, mô hình sẽ tự động được tuần tự hóa trước khi được đặt vào hàng đợi và được truy xuất lại từ cơ sở dữ liệu khi công việc được xử lý. Tuy nhiên, nếu mô hình đã bị xóa trong khi công việc đang chờ xử lý bởi nhân viên, công việc của bạn có thể không thành công với a ModelNotFoundException
.
Để thuận tiện, bạn có thể chọn tự động xóa các công việc có mô hình bị thiếu bằng cách đặt thuộc tính công việc của bạn deleteWhenMissingModels
thành true
. Khi thuộc tính này được đặt thành true
, Laravel sẽ lặng lẽ loại bỏ công việc mà không đưa ra ngoại lệ:
/**
* Delete the job if its models no longer exist.
*
* @var bool
*/
public $deleteWhenMissingModels = true;
Lưu trữ Công việc Không thành công trong DynamoDB
Laravel cũng cung cấp hỗ trợ lưu trữ hồ sơ công việc bị lỗi của bạn trong DynamoDB thay vì bảng cơ sở dữ liệu quan hệ. Tuy nhiên, bạn phải tạo một bảng DynamoDB để lưu trữ tất cả các bản ghi công việc bị lỗi. Thông thường, bảng này nên được đặt tên failed_jobs
, nhưng bạn nên đặt tên bảng dựa trên giá trị của queue.failed.table
giá trị cấu hình trong queue
tệp cấu hình ứng dụng của bạn .
Các failed_jobs
bảng cần phải có một phím chuỗi phân vùng chính được đặt tên application
và một chuỗi chìa khóa sắp xếp đầu tiên được đặt tên uuid
. Các application
phần của khóa sẽ chứa tên của ứng dụng của bạn theo quy định của name
giá trị cấu hình trong ứng dụng của bạn app
tập tin cấu hình. Vì tên ứng dụng là một phần của khóa của bảng DynamoDB, bạn có thể sử dụng cùng một bảng để lưu trữ các công việc bị lỗi cho nhiều ứng dụng Laravel.
Ngoài ra, hãy đảm bảo rằng bạn cài đặt AWS SDK để ứng dụng Laravel của bạn có thể giao tiếp với Amazon DynamoDB:
composer require aws/aws-sdk-php
Tiếp theo, đặt queue.failed.driver
giá trị của tùy chọn cấu hình thành dynamodb
. Bên cạnh đó, bạn nên xác định key
, secret
và region
tùy chọn cấu hình trong mảng cấu hình công việc thất bại. Các tùy chọn này sẽ được sử dụng để xác thực với AWS. Khi sử dụng dynamodb
trình điều khiển, queue.failed.database
tùy chọn cấu hình là không cần thiết:
'failed' => [
'driver' => env('QUEUE_FAILED_DRIVER', 'dynamodb'),
'key' => env('AWS_ACCESS_KEY_ID'),
'secret' => env('AWS_SECRET_ACCESS_KEY'),
'region' => env('AWS_DEFAULT_REGION', 'us-east-1'),
'table' => 'failed_jobs',
],
Công việc không thành công trong việc cắt tỉa
Bạn có thể xóa tất cả các bản ghi trong failed_jobs
bảng ứng dụng của mình bằng cách gọi queue:prune-failed
lệnh Artisan:
php artisan queue:prune-failed
Nếu bạn cung cấp --hours
tùy chọn cho lệnh, chỉ các bản ghi công việc bị lỗi đã được chèn trong N số giờ qua mới được giữ lại. Ví dụ: lệnh sau sẽ xóa tất cả các bản ghi công việc bị lỗi đã được chèn hơn 48 giờ trước:
php artisan queue:prune-failed --hours=48
Sự kiện việc làm không thành công
Nếu bạn muốn đăng ký một bộ lắng nghe sự kiện sẽ được gọi khi công việc không thành công, bạn có thể sử dụng phương thức Queue
của mặt tiền failing
. Ví dụ: chúng tôi có thể đính kèm một bao đóng cho sự kiện này từ boot
phương thức của sự AppServiceProvider
kiện được bao gồm trong Laravel:
<?php
namespace App\Providers;
use Illuminate\Support\Facades\Queue;
use Illuminate\Support\ServiceProvider;
use Illuminate\Queue\Events\JobFailed;
class AppServiceProvider extends ServiceProvider
{
/**
* Register any application services.
*
* @return void
*/
public function register()
{
//
}
/**
* Bootstrap any application services.
*
* @return void
*/
public function boot()
{
Queue::failing(function (JobFailed $event) {
// $event->connectionName
// $event->job
// $event->exception
});
}
}
Xóa công việc khỏi hàng đợi
Khi sử dụng Horizon , bạn nên sử dụng
horizon:clear
lệnh để xóa các công việc khỏi hàng đợi thay vìqueue:clear
lệnh.
Nếu bạn muốn xóa tất cả các công việc khỏi hàng đợi mặc định của kết nối mặc định, bạn có thể làm như vậy bằng cách sử dụng queue:clear
lệnh Artisan:
php artisan queue:clear
Bạn cũng có thể cung cấp connection
đối số và queue
tùy chọn để xóa công việc khỏi một kết nối và hàng đợi cụ thể:
php artisan queue:clear redis --queue=emails
Xóa công việc khỏi hàng đợi chỉ khả dụng cho trình điều khiển hàng đợi SQS, Redis và cơ sở dữ liệu. Ngoài ra, quá trình xóa tin nhắn SQS mất tới 60 giây, do đó, các công việc được gửi đến hàng đợi SQS lên đến 60 giây sau khi bạn xóa hàng đợi cũng có thể bị xóa.
Giám sát hàng đợi của bạn
Nếu hàng đợi của bạn nhận được một lượng công việc đột ngột, nó có thể trở nên quá tải, dẫn đến thời gian chờ đợi lâu để hoàn thành công việc. Nếu bạn muốn, Laravel có thể cảnh báo bạn khi số lượng công việc trong hàng đợi của bạn vượt quá ngưỡng đã chỉ định.
Để bắt đầu, bạn nên lập lịch queue:monitor
để lệnh chạy mỗi phút . Lệnh chấp nhận tên của hàng đợi bạn muốn theo dõi cũng như ngưỡng số lượng công việc mong muốn của bạn:
php artisan queue:monitor redis:default,redis:deployments --max=100
Chỉ lập lịch lệnh này thôi là không đủ để kích hoạt thông báo cảnh báo bạn về tình trạng quá tải của hàng đợi. Khi lệnh gặp hàng đợi có số lượng công việc vượt quá ngưỡng của bạn, một Illuminate\Queue\Events\QueueBusy
sự kiện sẽ được thực hiện. Bạn có thể lắng nghe sự kiện này trong ứng dụng của EventServiceProvider
mình để gửi thông báo cho bạn hoặc nhóm phát triển của bạn:
use App\Notifications\QueueHasLongWaitTime;
use Illuminate\Queue\Events\QueueBusy;
use Illuminate\Support\Facades\Event;
use Illuminate\Support\Facades\Notification;
/**
* Register any other events for your application.
*
* @return void
*/
public function boot()
{
Event::listen(function (QueueBusy $event) {
Notification::route('mail', 'dev@example.com')
->notify(new QueueHasLongWaitTime(
$event->connection,
$event->queue,
$event->size
));
});
}
Sự kiện việc làm
Sử dụng các phương thức before
và after
trên Queue
mặt tiền , bạn có thể chỉ định các lệnh gọi lại được thực thi trước hoặc sau khi xử lý công việc xếp hàng đợi. Các lệnh gọi lại này là cơ hội tuyệt vời để thực hiện ghi nhật ký bổ sung hoặc thống kê gia tăng cho trang tổng quan. Thông thường, bạn nên gọi các phương thức này từ boot
phương thức của nhà cung cấp dịch vụ . Ví dụ: chúng tôi có thể sử dụng AppServiceProvider
cái được bao gồm trong Laravel:
<?php
namespace App\Providers;
use Illuminate\Support\Facades\Queue;
use Illuminate\Support\ServiceProvider;
use Illuminate\Queue\Events\JobProcessed;
use Illuminate\Queue\Events\JobProcessing;
class AppServiceProvider extends ServiceProvider
{
/**
* Register any application services.
*
* @return void
*/
public function register()
{
//
}
/**
* Bootstrap any application services.
*
* @return void
*/
public function boot()
{
Queue::before(function (JobProcessing $event) {
// $event->connectionName
// $event->job
// $event->job->payload()
});
Queue::after(function (JobProcessed $event) {
// $event->connectionName
// $event->job
// $event->job->payload()
});
}
}
Sử dụng looping
phương thức trên Queue
mặt tiền , bạn có thể chỉ định các lệnh gọi lại thực thi trước khi worker cố gắng tìm nạp một công việc từ một hàng đợi. Ví dụ: bạn có thể đăng ký một lần đóng để khôi phục bất kỳ giao dịch nào bị bỏ ngỏ bởi một công việc không thành công trước đó:
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Queue;
Queue::looping(function () {
while (DB::transactionLevel() > 0) {
DB::rollBack();
}
});
Giải phóng thời gian, khai phóng năng lực