انتقل إلى المحتوى

المهام

المهام هي وحدة العمل الأساسية في Queuety. أرسل مهمة باسم معالج وحمولة، فيلتقطها عامل وينفذها في الخلفية.

إرسال المهام

PHP
use Queuety\Queuety;

Queuety::dispatch( 'send_email', [ 'to' => 'user@example.com' ] );

يعيد dispatch() أداة إنشاء من النوع PendingJob. يمكنك تسلسل الخيارات قبل الإرسال التلقائي:

PHP
use Queuety\Enums\Priority;

Queuety::dispatch( 'send_email', [ 'to' => 'user@example.com' ] )
    ->on_queue( 'emails' )
    ->with_priority( Priority::High )
    ->delay( 300 )
    ->max_attempts( 5 );

للحصول على معرّف المهمة فورًا، استدعِ ->id():

PHP
$job_id = Queuety::dispatch( 'send_email', $payload )->id();

كتابة المعالجات

المعالج هو أي فئة تنفّذ الواجهة Queuety\Handler:

PHP
use Queuety\Handler;

class SendEmailHandler implements Handler {
    public function handle( array $payload ): void {
        wp_mail( $payload['to'], $payload['subject'], $payload['body'] );
    }

    public function config(): array {
        return [
            'queue'           => 'emails',
            'max_attempts'    => 5,
            'backoff'         => 'exponential',
        ];
    }
}

تستقبل الطريقة handle() حمولة المهمة وتنفذ العمل. وتعيد الطريقة config() القيم الافتراضية الاختيارية:

المفتاح النوع الوصف
queue string اسم صف الانتظار الافتراضي
max_attempts int الحد الأقصى لعدد مرات التشغيل، بما فيها المرة الأولى، قبل نقل المهمة إلى الأرشيف
backoff string استراتيجية التراجع لإعادة المحاولة: exponential أو linear أو fixed
rate_limit array حد المعدل بصيغة [max_executions, window_seconds]
concurrency_group string مجموعة قبول مشتركة للعمال مخصصة للمعالجات المكلفة
concurrency_limit int الحد الأقصى لعدد مهام المعالجة المتزامنة داخل تلك المجموعة
cost_units int تكلفة التنفيذ النسبية المستخدمة في ميزانيات صفوف الانتظار أو المزوّد، وميزانيات سير العمل، وإرشادات القبول

تُعد مفاتيح الموارد هذه جزءًا من نموذج إدارة الموارد الأوسع في Queuety، حيث تعمل بيانات المهام الوصفية وميزانيات سير العمل وقبول العامل للمهام معًا.

سجّل المعالج:

PHP
Queuety::register( 'send_email', SendEmailHandler::class );

أو استخدم سمات PHP للتسجيل التلقائي.

الأولوية

للمهام أربعة مستويات أولوية. يطالب العمال بالمهام ذات الأولوية الأعلى أولًا.

PHP
use Queuety\Enums\Priority;

Queuety::dispatch( 'handler', $payload )->with_priority( Priority::Urgent );
المستوى القيمة الوصف
Priority::Low 0 الافتراضي. عمل في الخلفية يمكنه الانتظار.
Priority::Normal 1 أولوية عادية.
Priority::High 2 يُعالج قبل المنخفض والعادي.
Priority::Urgent 3 يُعالج أولًا. استخدمه باعتدال.

التأخير

أخّر المهمة لتصبح متاحة بعد انقضاء عدد الثواني المحدد فقط:

PHP
Queuety::dispatch( 'send_reminder', $payload )->delay( 3600 ); // 1 hour

تُضاف المهمة فورًا، لكن العمال لن يطالبوا بها حتى انقضاء مدة التأخير.

المهام الفريدة

امنع إرسال نسخ مكررة من المعالج والحمولة نفسيهما. إذا كانت هناك مهمة معلّقة أو قيد المعالجة لها المعالج والحمولة نفسيهما، فسيُعاد معرّف المهمة الموجودة بدلًا من إنشاء مهمة جديدة.

PHP
Queuety::dispatch( 'sync_inventory', [ 'sku' => 'ABC-123' ] )->unique();

تبعيات المهام

يمكن أن تعتمد مهمة على مهمة أخرى. ولن يطالب بها عامل حتى تكتمل المهمة التابعة لها.

PHP
$first_id = Queuety::dispatch( 'import_data', $payload )->id();

Queuety::dispatch( 'send_notification', [ 'import_id' => $first_id ] )
    ->after( $first_id );

إرسال دفعة

أرسل عدة مهام ضمن عملية INSERT واحدة متعددة الصفوف لتحقيق أعلى إنتاجية:

PHP
$job_ids = Queuety::batch( [
    [ 'handler' => 'send_email', 'payload' => [ 'to' => 'a@example.com' ] ],
    [ 'handler' => 'send_email', 'payload' => [ 'to' => 'b@example.com' ] ],
    [ 'handler' => 'send_email', 'payload' => [ 'to' => 'c@example.com' ] ],
] );

يمكن أن يتضمن كل عنصر مفاتيح اختيارية: queue وpriority وdelay وmax_attempts.

إعادة المحاولة والتراجع

عندما ترمي المهمة استثناءً، يعيد Queuety المهمة إلى pending مع تأخير available_at حتى تُشغّل max_attempts مرات إجمالًا (تُحتسب المرة الأولى، لذا تعني max_attempts( 3 ) إجراء محاولتي إعادة كحد أقصى). تحدد استراتيجية التراجع مدة الانتظار قبل كل إعادة محاولة، حيث يشير attempt إلى رقم التشغيل الذي فشل للتو:

الاستراتيجية معادلة التأخير مثال (بعد التشغيل 1 و2 و3)
exponential 2^attempt * 30 ثانية، بحد أقصى 3600 60s، 120s، 240s
linear attempt * 60 ثانية 60s، 120s، 180s
fixed 60 ثانية 60s، 60s، 60s

عيّن القيمة الافتراضية العامة في wp-config.php:

PHP
define( 'QUEUETY_RETRY_BACKOFF', 'linear' );

أو لكل معالج في config():

PHP
public function config(): array {
    return [ 'backoff' => 'exponential', 'max_attempts' => 5 ];
}

أو لكل عملية إرسال:

PHP
Queuety::dispatch( 'handler', $payload )->max_attempts( 10 );

المهام المدفونة

بعد استنفاد جميع محاولات إعادة التشغيل، تُعلَّم المهمة بأنها مدفونة. تبقى المهام المدفونة في قاعدة البيانات للفحص، لكن لا تُعالَج. يمكنك إعادة تشغيلها يدويًا. تجعل إعادة التشغيل اليدوية المهمة pending مجددًا من دون إعادة ضبط عدد محاولاتها، لذا تحصل المهمة المدفونة التي أُعيد تشغيلها على فرصة تنفيذ أخرى، ثم تُدفن مجددًا إذا فشلت تلك المحاولة:

PHP
// Retry a specific buried job
Queuety::retry( $job_id );

// Retry all buried jobs
$count = Queuety::retry_buried();

أو عبر CLI:

سطر الأوامر
wp queuety retry 42
wp queuety retry-buried

المهلة الزمنية

يقوم Queuety بتفعيل pcntl_alarm() لمدة التنفيذ القصوى، ويُفترض أن يؤدي تجاوزها إلى فشل المحاولة مع TimeoutException. لكن هذا المهلة غير مطبقة حاليًا (#10): لا يفعّل العامل الإشارات غير المتزامنة، لذا يكتمل المعالج طويل التشغيل، بينما يُوقَف المعالج العالق في sleep() عند إطلاق التنبيه ثم يُعلَّم كمكتمل. اضبط المهلة على مستوى عام:

PHP
define( 'QUEUETY_MAX_EXECUTION_TIME', 300 ); // 5 minutes

تُستعاد تلقائيًا المهام التي تبقى في الحالة processing لأكثر من QUEUETY_STALE_TIMEOUT ثانية:

PHP
define( 'QUEUETY_STALE_TIMEOUT', 600 ); // 10 minutes

استرداد المهام القديمة يدويًا:

سطر الأوامر
wp queuety recover

قوائم الانتظار

تُرسَل المهام إلى قوائم انتظار مسماة. يعالج كل عامل قائمة انتظار واحدة أو قائمة مرتبة من قوائم الانتظار (راجع Workers):

PHP
Queuety::dispatch( 'handler', $payload )->on_queue( 'emails' );
سطر الأوامر
wp queuety work --queue=emails

قائمة الانتظار الافتراضية هي default. يمكنك تشغيل عمليات عامل منفصلة لكل قائمة انتظار لعزل أحمال العمل.

تحديد معدل الطلبات

حدّد عدد مرات تنفيذ المعالج خلال فترة زمنية:

PHP
Queuety::dispatch( 'call_api', $payload )
    ->rate_limit( 10, 60 ); // max 10 executions per 60 seconds

راجع Rate Limiting للاطلاع على التفاصيل.

فئات المهام القابلة للإرسال

يقدّم Queuety v0.6.0 مفهوم مهمة مبني على الفئات عبر API. فبدلًا من فصل اسم المعالج والحمولة وفئة المعالج، تعرّف فئة واحدة تضم كل شيء: الحمولة (على هيئة خصائص عامة ذات أنواع محددة)، ومنطق التنفيذ، والبرمجيات الوسيطة الاختيارية.

الواجهة Contracts\Job

PHP
use Queuety\Contracts\Job;

interface Job {
    public function handle(): void;
}

تحتفظ فئة Job بحمولتها في خصائص عامة، وتنفّذ منطقها الخاص في handle(). ولا يلزم وجود طريقة config() منفصلة أو تسجيل للمعالج.

السمة Dispatchable

أضف السمة Dispatchable للحصول على طريقة ثابتة dispatch() في فئة المهمة:

PHP
use Queuety\Contracts\Job;
use Queuety\Dispatchable;

class SendEmailJob implements Job {
    use Dispatchable;

    public function __construct(
        public readonly string $to,
        public readonly string $subject,
        public readonly string $body,
    ) {}

    public function handle(): void {
        wp_mail( $this->to, $this->subject, $this->body );
    }
}

أرسلها بتمرير وسائط المُنشئ:

PHP
SendEmailJob::dispatch( 'user@example.com', 'Welcome!', 'Hello there.' )
    ->on_queue( 'emails' )
    ->with_priority( Priority::High );

تستدعي السمة Dispatchable داخليًا Queuety::dispatch()، لذا تعمل جميع خيارات PendingJob (قائمة الانتظار، والأولوية، والتأخير، وmax_attempts، وunique، وafter) بالطريقة نفسها تمامًا.

آلية التسلسل

عند إرسال نسخة من Job، تستخرج Queuety JobSerializer اسم الفئة الكامل بوصفه المعالج، وجميع الخصائص العامة بوصفها الحمولة. وعلى جانب العامل، يعيد المُسلسِل إنشاء النسخة بمطابقة مفاتيح الحمولة مع أسماء وسائط المُنشئ. تُسلسَل التعدادات ذات القيم الأساسية إلى قيمها الأصلية وتُستعاد تلقائيًا.

خصائص المهمة

يمكن لفئات المهام تعريف خصائص عامة للتحكم في سلوك إعادة المحاولة والمهلة. يقرأها العامل عبر الانعكاس ويطبقها تلقائيًا.

PHP
use Queuety\Contracts\Job;
use Queuety\Dispatchable;

class ImportProductsJob implements Job {
    use Dispatchable;

    public int $tries = 5;
    public int $timeout = 120;
    public array $backoff = [ 10, 30, 60, 120 ];
    public string $concurrency_group = 'imports';
    public int $concurrency_limit = 2;
    public int $cost_units = 4;

    public function __construct(
        public readonly int $supplier_id,
    ) {}

    public function handle(): void {
        // Import products...
    }
}
الخاصية النوع الوصف
$tries int الحد الأقصى لعدد المحاولات قبل دفن المهمة. يتجاوز قيمة max_attempts المحددة عند الإرسال.
$timeout int الحد الأقصى لمدة التنفيذ بالثواني. يتجاوز الثابت العام QUEUETY_MAX_EXECUTION_TIME. يتطلب pcntl، ولم يُطبَّق بعد (راجع Timeout).
$backoff array مصفوفة قيم التأخير بالثواني لكل محاولة إعادة تشغيل. يُعاد استخدام القيمة الأخيرة للمحاولات اللاحقة.
$concurrency_group string مجموعة مشتركة لقبول المهام المكلفة لدى العامل.
$concurrency_limit int الحد الأقصى للمهام المتزامنة من نوع processing في تلك المجموعة. يُتجاهل دون $concurrency_group.
$cost_units int التكلفة النسبية للتنفيذ، وتُستخدم في ميزانيات سير العمل وقبول العامل.

توفر مصفوفة $backoff فترات تأخير متزايدة لإعادة المحاولة. مثلًا، تعني [10, 30, 60, 120] أن الانتظار للمحاولة الأولى 10 ثوانٍ، وللثانية 30 ثانية، وللثالثة 60 ثانية، ولأي محاولات إضافية 120 ثانية.

تكون الخصائص المتعلقة بالموارد مفيدة عندما تمثل فئات المهام مستويات تكلفة مختلفة. فتحديث ذاكرة تخزين مؤقت بسرعة واستدعاء مزود كبير قد يُعدّ كل منهما «مهمة واحدة»، لكن لا ينبغي بالضرورة احتسابهما بالطريقة نفسها عندما تحاول ميزانية سير العمل أو قرارات قبول العامل إبقاء العملية ضمن نطاق آمن.

للاطلاع على النموذج كاملًا، بما يشمل ميزانيات سير العمل وقبول المهام لدى العامل، راجع Resource Management.

الخطاف عند الفشل

عندما تُدفن مهمة بعد استنفاد جميع محاولات إعادة التشغيل، يستدعي Queuety طريقة failed() في نسخة المهمة إن كانت معرّفة. استخدمها للتنظيف أو التنبيه أو التسجيل.

PHP
use Queuety\Contracts\Job;
use Queuety\Dispatchable;

class ProcessPaymentJob implements Job {
    use Dispatchable;

    public int $tries = 3;

    public function __construct(
        public readonly int $order_id,
    ) {}

    public function handle(): void {
        // Process the payment...
    }

    public function failed( \Throwable $exception ): void {
        // Notify the team that payment processing failed permanently.
        error_log( "Payment failed for order {$this->order_id}: {$exception->getMessage()}" );
    }
}

تتلقى الطريقة failed() الاستثناء الذي تسبب في الفشل النهائي. يُعاد إلغاء تسلسل نسخة المهمة بحمولتها الأصلية، فتكون جميع الخصائص العامة متاحة.

الإرسال المشروط

توفر السمة Dispatchable الطريقتين dispatch_if() وdispatch_unless() للإرسال المشروط. تقبل كلتاهما شرطًا منطقيًا كوسيط أول، تليه وسائط مُنشئ المهمة.

PHP
// Only dispatch if the user has opted in to notifications.
SendEmailJob::dispatch_if( $user->wants_notifications, $user->email, 'Update', $body );

// Dispatch unless the feature flag is disabled.
SyncInventoryJob::dispatch_unless( $maintenance_mode, $sku );

ترسل dispatch_if() المهمة عندما يكون الشرط true، وتعيد PendingJob. وتعيد null عندما يكون الشرط false.

ترسل dispatch_unless() المهمة عندما يكون الشرط false، وتعيد PendingJob. وتعيد null عندما يكون الشرط true.

تدعم الطريقتان إضافة خيارات PendingJob عند الإرسال:

PHP
SendEmailJob::dispatch_if( $should_send, $to, $subject, $body )
    ?->on_queue( 'emails' )
    ?->with_priority( Priority::High );

الإرسال المتزامن

استخدم Queuety::dispatch_sync() لتنفيذ مهمة فورًا في العملية الحالية دون إضافتها إلى قائمة الانتظار. يفيد ذلك في الاختبار وللمهام التي يجب تنفيذها مباشرةً.

PHP
use Queuety\Queuety;

// With a Job instance
Queuety::dispatch_sync( new SendEmailJob( to: 'user@example.com', subject: 'Hello', body: 'Hi!' ) );

// With a handler name and payload
Queuety::dispatch_sync( 'send_email', [ 'to' => 'user@example.com' ] );

يُستدعى أسلوب handle() الخاص بالمهمة مباشرة. لا يُنشأ أي سجل في قاعدة البيانات، ولا يُستخدم أي عامل، ولا تُطبّق البرمجيات الوسيطة.

تسلسل المهام

ينفذ تسلسل المهام مجموعة من المهام واحدة تلو الأخرى. تعتمد كل مهمة في التسلسل على إكمال المهمة السابقة بنجاح. إذا فشلت أي مهمة، تظل المهام اللاحقة في التسلسل معلقة.

استخدام Queuety::chain()

PHP
use Queuety\Queuety;

$first_job_id = Queuety::chain( [
    new FetchDataJob( $url ),
    new ProcessDataJob(),
    new NotifyCompleteJob(),
] )
    ->on_queue( 'pipeline' )
    ->catch( ChainFailedHandler::class )
    ->dispatch();

تعيد Queuety::chain() كائن ChainBuilder يضم طرقًا متسلسلة لضبط قائمة الانتظار ومعالج الفشل. تنشئ الطريقة dispatch() جميع المهام بعلاقات depends_on متتابعة، وتعيد معرّف المهمة الأولى.

استخدام with_chain()

توفر السمة Dispatchable طريقة with_chain(). وهي حاليًا مكافئة لـ Queuety::chain( $jobs ): لا تُضاف الفئة التي استُدعيت عليها إلى التسلسل (#13)، لذا أدرج المهمة الأولى في المصفوفة أيضًا:

PHP
FetchDataJob::with_chain( [
    new FetchDataJob(),
    new ProcessDataJob(),
    new NotifyCompleteJob(),
] )
    ->on_queue( 'pipeline' )
    ->dispatch();

معالجة فشل التسلسل

سجّل فئة لمعالجة الفشل باستخدام catch(). يجب أن تحتوي فئة المعالج على طريقة handle(). تُستدعى عند دفن أي مهمة في التسلسل نهائيًا. يدعم العامل handle() وhandle( Job $job ) وhandle( Job $job, \Throwable $exception ).

PHP
class ChainFailedHandler {
    public function handle(): void {
        // Alert, clean up, etc.
    }
}

إضافة برمجيات وسيطة

يمكن لفئات المهام تعريف طريقة middleware() لإرجاع برمجيات وسيطة تحيط بالتنفيذ:

PHP
use Queuety\Contracts\Job;
use Queuety\Dispatchable;
use Queuety\Middleware\RateLimited;
use Queuety\Middleware\Timeout;

class CallOpenAIJob implements Job {
    use Dispatchable;

    public function __construct(
        public readonly string $prompt,
    ) {}

    public function handle(): void {
        // Call the API...
    }

    public function middleware(): array {
        return [
            new RateLimited( max: 60, window: 60 ),
            new Timeout( seconds: 120 ),
        ];
    }
}

راجع Middleware للاطلاع على تفاصيل البرمجيات الوسيطة المضمنة وكتابة برمجيات وسيطة مخصصة.

مقارنة بالمهام المعتمدة على المعالجات

المعتمدة على المعالج (v0.1+) المعتمدة على الفئة (v0.6+)
الإرسال Queuety::dispatch( 'send_email', $payload ) SendEmailJob::dispatch( $to, $subject )
المعالج فئة منفصلة تطبق Handler الفئة نفسها تطبق Contracts\Job
الحمولة array $payload بلا أنواع محددة خصائص عامة ذات أنواع محددة
التسجيل Queuety::register() أو #[QueuetyHandler] غير مطلوب (يستخدم FQCN)
البرمجيات الوسيطة غير متاحة طريقة اختيارية middleware()
الإعداد طريقة config() خصائص المهمة + سلسلة PendingJob

كلا الأسلوبين مدعومان بالكامل. وتواصل المهام الحالية المعتمدة على المعالجات العمل دون تغييرات. ويمكنك الجمع بين الأسلوبين في المشروع نفسه.

دورة حياة المهمة

الكود
dispatch() -> pending -> processing -> completed
                  |           |
                  |           +-> exception -> pending (retry after backoff)
                  |                       |
                  |                       +-> buried (max attempts reached)
                  |
                  +-> delayed (available_at in the future)
الحالة الوصف
pending في انتظار استلامها من عامل
processing قيد التنفيذ حاليًا
completed اكتملت بنجاح
failed محجوزة؛ لا يضبطها العامل حاليًا. تعود المحاولة الفاشلة التي ستُعاد إلى pending
buried استُنفدت جميع محاولات إعادة التشغيل