المهام
المهام هي وحدة العمل الأساسية في Queuety. أرسل مهمة باسم معالج وحمولة، فيلتقطها عامل وينفذها في الخلفية.
إرسال المهام
use Queuety\Queuety;
Queuety::dispatch( 'send_email', [ 'to' => 'user@example.com' ] );
يعيد dispatch() أداة إنشاء من النوع PendingJob. يمكنك تسلسل الخيارات قبل الإرسال التلقائي:
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():
$job_id = Queuety::dispatch( 'send_email', $payload )->id();
كتابة المعالجات
المعالج هو أي فئة تنفّذ الواجهة Queuety\Handler:
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، حيث تعمل بيانات المهام الوصفية وميزانيات سير العمل وقبول العامل للمهام معًا.
سجّل المعالج:
Queuety::register( 'send_email', SendEmailHandler::class );
أو استخدم سمات 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 |
يُعالج أولًا. استخدمه باعتدال. |
التأخير
أخّر المهمة لتصبح متاحة بعد انقضاء عدد الثواني المحدد فقط:
Queuety::dispatch( 'send_reminder', $payload )->delay( 3600 ); // 1 hour
تُضاف المهمة فورًا، لكن العمال لن يطالبوا بها حتى انقضاء مدة التأخير.
المهام الفريدة
امنع إرسال نسخ مكررة من المعالج والحمولة نفسيهما. إذا كانت هناك مهمة معلّقة أو قيد المعالجة لها المعالج والحمولة نفسيهما، فسيُعاد معرّف المهمة الموجودة بدلًا من إنشاء مهمة جديدة.
Queuety::dispatch( 'sync_inventory', [ 'sku' => 'ABC-123' ] )->unique();
تبعيات المهام
يمكن أن تعتمد مهمة على مهمة أخرى. ولن يطالب بها عامل حتى تكتمل المهمة التابعة لها.
$first_id = Queuety::dispatch( 'import_data', $payload )->id();
Queuety::dispatch( 'send_notification', [ 'import_id' => $first_id ] )
->after( $first_id );
إرسال دفعة
أرسل عدة مهام ضمن عملية INSERT واحدة متعددة الصفوف لتحقيق أعلى إنتاجية:
$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:
define( 'QUEUETY_RETRY_BACKOFF', 'linear' );
أو لكل معالج في config():
public function config(): array {
return [ 'backoff' => 'exponential', 'max_attempts' => 5 ];
}
أو لكل عملية إرسال:
Queuety::dispatch( 'handler', $payload )->max_attempts( 10 );
المهام المدفونة
بعد استنفاد جميع محاولات إعادة التشغيل، تُعلَّم المهمة بأنها مدفونة. تبقى المهام المدفونة في قاعدة البيانات للفحص، لكن لا تُعالَج. يمكنك إعادة تشغيلها يدويًا. تجعل إعادة التشغيل اليدوية المهمة pending مجددًا من دون إعادة ضبط عدد محاولاتها، لذا تحصل المهمة المدفونة التي أُعيد تشغيلها على فرصة تنفيذ أخرى، ثم تُدفن مجددًا إذا فشلت تلك المحاولة:
// 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() عند إطلاق التنبيه ثم يُعلَّم كمكتمل. اضبط المهلة على مستوى عام:
define( 'QUEUETY_MAX_EXECUTION_TIME', 300 ); // 5 minutes
تُستعاد تلقائيًا المهام التي تبقى في الحالة processing لأكثر من QUEUETY_STALE_TIMEOUT ثانية:
define( 'QUEUETY_STALE_TIMEOUT', 600 ); // 10 minutes
استرداد المهام القديمة يدويًا:
wp queuety recover
قوائم الانتظار
تُرسَل المهام إلى قوائم انتظار مسماة. يعالج كل عامل قائمة انتظار واحدة أو قائمة مرتبة من قوائم الانتظار (راجع Workers):
Queuety::dispatch( 'handler', $payload )->on_queue( 'emails' );
wp queuety work --queue=emails
قائمة الانتظار الافتراضية هي default. يمكنك تشغيل عمليات عامل منفصلة لكل قائمة انتظار لعزل أحمال العمل.
تحديد معدل الطلبات
حدّد عدد مرات تنفيذ المعالج خلال فترة زمنية:
Queuety::dispatch( 'call_api', $payload )
->rate_limit( 10, 60 ); // max 10 executions per 60 seconds
راجع Rate Limiting للاطلاع على التفاصيل.
فئات المهام القابلة للإرسال
يقدّم Queuety v0.6.0 مفهوم مهمة مبني على الفئات عبر API. فبدلًا من فصل اسم المعالج والحمولة وفئة المعالج، تعرّف فئة واحدة تضم كل شيء: الحمولة (على هيئة خصائص عامة ذات أنواع محددة)، ومنطق التنفيذ، والبرمجيات الوسيطة الاختيارية.
الواجهة Contracts\Job
use Queuety\Contracts\Job;
interface Job {
public function handle(): void;
}
تحتفظ فئة Job بحمولتها في خصائص عامة، وتنفّذ منطقها الخاص في handle(). ولا يلزم وجود طريقة config() منفصلة أو تسجيل للمعالج.
السمة Dispatchable
أضف السمة Dispatchable للحصول على طريقة ثابتة dispatch() في فئة المهمة:
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 );
}
}
أرسلها بتمرير وسائط المُنشئ:
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 اسم الفئة الكامل بوصفه المعالج، وجميع الخصائص العامة بوصفها الحمولة. وعلى جانب العامل، يعيد المُسلسِل إنشاء النسخة بمطابقة مفاتيح الحمولة مع أسماء وسائط المُنشئ. تُسلسَل التعدادات ذات القيم الأساسية إلى قيمها الأصلية وتُستعاد تلقائيًا.
خصائص المهمة
يمكن لفئات المهام تعريف خصائص عامة للتحكم في سلوك إعادة المحاولة والمهلة. يقرأها العامل عبر الانعكاس ويطبقها تلقائيًا.
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() في نسخة المهمة إن كانت معرّفة. استخدمها للتنظيف أو التنبيه أو التسجيل.
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() للإرسال المشروط. تقبل كلتاهما شرطًا منطقيًا كوسيط أول، تليه وسائط مُنشئ المهمة.
// 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 عند الإرسال:
SendEmailJob::dispatch_if( $should_send, $to, $subject, $body )
?->on_queue( 'emails' )
?->with_priority( Priority::High );
الإرسال المتزامن
استخدم Queuety::dispatch_sync() لتنفيذ مهمة فورًا في العملية الحالية دون إضافتها إلى قائمة الانتظار. يفيد ذلك في الاختبار وللمهام التي يجب تنفيذها مباشرةً.
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()
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)، لذا أدرج المهمة الأولى في المصفوفة أيضًا:
FetchDataJob::with_chain( [
new FetchDataJob(),
new ProcessDataJob(),
new NotifyCompleteJob(),
] )
->on_queue( 'pipeline' )
->dispatch();
معالجة فشل التسلسل
سجّل فئة لمعالجة الفشل باستخدام catch(). يجب أن تحتوي فئة المعالج على طريقة handle(). تُستدعى عند دفن أي مهمة في التسلسل نهائيًا. يدعم العامل handle() وhandle( Job $job ) وhandle( Job $job, \Throwable $exception ).
class ChainFailedHandler {
public function handle(): void {
// Alert, clean up, etc.
}
}
إضافة برمجيات وسيطة
يمكن لفئات المهام تعريف طريقة middleware() لإرجاع برمجيات وسيطة تحيط بالتنفيذ:
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 |
استُنفدت جميع محاولات إعادة التشغيل |