Laravel: Hàng đợi (Queues)

Các khóa học qua video:
Python SQL Server PHP C# Lập trình C Java HTML5-CSS3-JavaScript
Học trên YouTube <76K/tháng. Đăng ký Hội viên
Viết nhanh hơn - Học tốt hơn
Giải phóng thời gian, khai phóng năng lực

 

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.phptệ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 nulltà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.phptệp cấu hình của bạn , có một connectionsmả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 queuetệp cấu hình chứa một queuethuộ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 queuethuộ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 highhà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 databasetrì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:tablelệ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 migratelệnh:

php artisan queue:table

php artisan migrate

 

Redis

Để sử dụng redistrì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.phptệ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_fortù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ị để 5chỉ 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_forthành 0sẽ 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.0hoặ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/Jobsthư mục. Nếu app/Jobsthư mục không tồn tại, nó sẽ được tạo khi bạn chạy make:joblệnh Artisan:

php artisan make:job ProcessPodcast

Lớp được tạo sẽ triển khai Illuminate\Contracts\Queue\ShouldQueuegiao 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ộ.

 

Sơ lược công việc có thể được tùy chỉnh bằng cách sử dụng xuất bản sơ khai .

 

 

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 handlephươ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 handlephươ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 handlephươ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 handlephương thức, bạn có thể sử dụng bindMethodphương thức của vùng chứa . Các bindMethodphươ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 handlephươ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ừ bootphươ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_encodehà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 withoutRelationsphươ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, memcachedredisdynamodbdatabasefile, và arraytrì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 ShouldBeUniquegiao 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, UpdateSearchIndexcô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 uniqueIdvà uniqueForcá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, UpdateSearchIndexcô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 ShouldBeUniqueUntilProcessinghợp đồng thay vì ShouldBeUniquehợ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 ShouldBeUniquecông việc được cử đi, Laravel cố gắng lấy một ổ khóa bằng uniqueIdchì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 uniqueViaphươ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 WithoutOverlappingphầ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 handlephươ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 handlephươ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/Middlewarethư 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ừ middlewarephươ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:joblệ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 RateLimitercủ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 RateLimitertrong bootphươ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 perMinutephươ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 byphươ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\RateLimitedphầ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 triesvà maxExceptionscá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 retryUntilphươ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 dontReleasephươ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\RateLimitedWithRedisphầ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\WithoutOverlappingphầ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 WithoutOverlappingphần mềm trung gian từ middlewarephươ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 dontReleasephươ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 WithoutOverlappingmề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 expireAfterphương pháp này. Ví dụ, ví dụ dưới đây sẽ hướng dẫn Laravel giải phóng WithoutOverlappingkhó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 WithoutOverlappingmềm trung gian yêu cầu trình điều khiển bộ nhớ cache hỗ trợ khóa . Hiện nay, memcachedredisdynamodbdatabasefile, và arraytrì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\ThrottlesExceptionsphầ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 ThrottlesExceptionsphần mềm trung gian từ middlewarephươ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 backoffphươ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 byphươ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\ThrottlesExceptionsWithRedisphầ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 dispatchphương pháp trên chính công việc đó. Các đối số được truyền cho dispatchphươ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 dispatchIfvà 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 delayphươ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, dispatchAfterResponsephươ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 afterResponsephương thức vào trình dispatchtrợ 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 dispatchSyncphươ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_committù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_committù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_committùy chọn cấu hình thành truecũ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_committù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 afterCommitphươ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_committù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 chainphương pháp được cung cấp bởi Busmặ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 onConnectionvà 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 catchphươ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 Throwabletrườ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 onQueuephươ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 onQueuephươ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 onConnectionphươ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 onConnectionvà onQueuevớ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 onConnectionphươ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 --triestắ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 --triesgiá 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 retryUntilphươ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 DateTimephiê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 triestính hoặc retryUntilphươ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 releasetrự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 maxExceptionstí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 pcntlmở 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 --timeouttắ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 $failOnTimeouttí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 --triestắc được sử dụng trên queue:worklệ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 releasephương thức:

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

    $this->release();
}

Theo mặc định, releasephươ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 releasephươ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 failphươ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 failphươ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-tablelệ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 batchphươ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 batchphương pháp của Busmặ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 thencatchvà finallyphươ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\Batchthể 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->idtí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 namephươ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 onConnectionvà 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 LoadImportBatchcô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 addphương thức trên phiên bản hàng loạt có thể được truy cập thông qua batchphươ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\Batchphiê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 Buscủ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 cancelphương thức trên Illuminate\Bus\Batchphiê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 handlephươ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 catchgọ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 allowFailuresphươ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-batchlệ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-batchlệ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_batchesbả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-batcheslệ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 hourstù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_batchesbả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-batcheslệ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 unfinishedtù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 catchphươ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:worklệ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:worklệnh Artisan. Lưu ý rằng khi queue:worklệ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:workquá 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:listenlệnh. Khi sử dụng queue:listenlệ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:worklệ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:workquy 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 numprocsgiá 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 worklệnh phải tương ứng với một trong các kết nối được xác định trong config/queue.phptệ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 emailshàng đợi trên rediskế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 --oncetù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-jobstù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-emptytù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-timetù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, sleeptù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ớ imagedestroykhi 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.phptệp cấu hình của bạn , bạn có thể đặt mặc định queuecho rediskế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 highhà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 highcô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 lowhà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 worklệ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:restartlệ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:restartlệ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.phptệp cấu hình của bạn , mỗi kết nối hàng đợi xác định một retry_aftertù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_aftergiá 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_aftergiá 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:workArtisan hiển thị một --timeouttù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_aftertùy chọn cấu hình và các --timeouttù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 --timeoutgiá trị nên luôn luôn có ít nhất là vài giây ngắn hơn của bạn retry_aftergiá 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 --timeouttùy chọn của bạn dài hơn retry_aftergiá 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:workquy trình của mình hoạt động. Một queue:workquá 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:restartlệ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:workquy 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:workquy 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:workquy 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.dthư 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.conftệp khởi động và giám sát queue:workcá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, numprocschỉ thị sẽ hướng dẫn Người giám sát chạy tám queue:workquy 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 commandchỉ 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 stopwaitsecslớ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_jobsbả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_jobsbảng, bạn có thể sử dụng queue:failed-tablelệ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 --triestắc trên queue:worklệnh. Nếu bạn không chỉ định giá trị cho --triestù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 --backofftù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 backofftí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 backoffphươ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ừ backoffphươ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 failedphương thức trên lớp công việc của mình. Phiên bản Throwablekhiến công việc không thành công sẽ được chuyển cho failedphươ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 failedphương thức; do đó, bất kỳ sửa đổi thuộc tính lớp nào có thể đã xảy ra trong handlephươ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_jobsbảng cơ sở dữ liệu của bạn , bạn có thể sử dụng queue:failedlệnh Artisan:

php artisan queue:failed

Các queue:failedlệ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:retrylệnh và chuyển alllà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:forgetlệnh:

php artisan queue:forget 5

 

Khi sử dụng Horizon , bạn nên sử dụng horizon:forgetlệnh xóa công việc bị lỗi thay vì queue:forgetlệ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_jobsbảng, bạn có thể sử dụng queue:flushlệ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 deleteWhenMissingModelsthà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.tablegiá trị cấu hình trong queuetệp cấu hình ứng dụng của bạn .

Các failed_jobsbảng cần phải có một phím chuỗi phân vùng chính được đặt tên applicationvà một chuỗi chìa khóa sắp xếp đầu tiên được đặt tên uuid. Các applicationphần của khóa sẽ chứa tên của ứng dụng của bạn theo quy định của namegiá trị cấu hình trong ứng dụng của bạn apptậ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.drivergiá 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 keysecretvà regiontù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 dynamodbtrình điều khiển, queue.failed.databasetù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_jobsbảng ứng dụng của mình bằng cách gọi queue:prune-failedlệnh Artisan:

php artisan queue:prune-failed

Nếu bạn cung cấp --hourstù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 Queuecủ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ừ bootphương thức của sự AppServiceProviderkiệ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:clearlệnh để xóa các công việc khỏi hàng đợi thay vì queue:clearlệ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:clearlệnh Artisan:

php artisan queue:clear

Bạn cũng có thể cung cấp connectionđối số và queuetù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\QueueBusysự 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 EventServiceProvidermì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 beforevà aftertrê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ừ bootphương thức của nhà cung cấp dịch vụ . Ví dụ: chúng tôi có thể sử dụng AppServiceProvidercá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 loopingphươ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();
    }
});
» Tiếp: Broadcasting
« Trước: Biên dịch các asset (Compiling Assets)
Các khóa học qua video:
Python SQL Server PHP C# Lập trình C Java HTML5-CSS3-JavaScript
Học trên YouTube <76K/tháng. Đăng ký Hội viên
Viết nhanh hơn - Học tốt hơn
Giải phóng thời gian, khai phóng năng lực
Copied !!!