إطار Laravel

جدولة المهام

15 دقيقة الدرس 19 من 45

جدولة المهام في Laravel

يوفر مجدول المهام في Laravel واجهة برمجة تطبيقات سلسة ومعبرة لتحديد المهام المجدولة داخل تطبيقك. باستخدام المجدول، تحتاج فقط إلى إدخال cron واحد على خادمك، ويتم تعريف المهام المجدولة في الكود، مما يجعلها خاضعة لإدارة الإصدارات وأسهل في الإدارة.

إعداد المجدول

أضف إدخال cron واحد إلى خادمك يقوم بتشغيل مجدول Laravel كل دقيقة:

# تحرير crontab crontab -e # أضف هذا السطر * * * * * cd /path-to-your-project && php artisan schedule:run >> /dev/null 2>&1 # للإنتاج، سجل الإخراج * * * * * cd /path-to-your-project && php artisan schedule:run >> /var/log/laravel-schedule.log 2>&1
ملاحظة: يعمل المجدول كل دقيقة ويتحقق من المهام التي حان موعد تشغيلها. إدخال cron الواحد هذا هو كل ما تحتاجه - يتعامل Laravel مع الباقي داخليًا.

تعريف الجداول

عرّف المهام المجدولة في طريقة schedule في app/Console/Kernel.php:

<?php namespace App\Console; use App\Jobs\GenerateMonthlyReport; use App\Jobs\ProcessSubscriptions; use Illuminate\Console\Scheduling\Schedule; use Illuminate\Foundation\Console\Kernel as ConsoleKernel; use Illuminate\Support\Facades\DB; class Kernel extends ConsoleKernel { protected function schedule(Schedule $schedule) { // تشغيل الأمر كل دقيقة $schedule->command('emails:send') ->everyMinute(); // تشغيل الأمر كل ساعة $schedule->command('reports:generate') ->hourly(); // تشغيل الأمر يوميًا في وقت محدد $schedule->command('users:cleanup') ->dailyAt('02:00'); // تشغيل مهمة $schedule->job(new ProcessSubscriptions) ->daily(); // تشغيل إغلاق $schedule->call(function () { DB::table('recent_activity')->delete(); })->daily(); // تنفيذ أمر shell $schedule->exec('node /home/forge/script.js') ->daily(); // وضع أمر في قائمة انتظار $schedule->command('emails:send') ->everyFiveMinutes() ->onOneServer() ->runInBackground(); } protected function commands() { $this->load(__DIR__.'/Commands'); require base_path('routes/console.php'); } }

خيارات تكرار الجدولة

يوفر Laravel العديد من خيارات التكرار لجدولة المهام:

// ترددات تعتمد على الوقت $schedule->command('command')->everyMinute(); // كل دقيقة $schedule->command('command')->everyTwoMinutes(); // كل دقيقتين $schedule->command('command')->everyFiveMinutes(); // كل 5 دقائق $schedule->command('command')->everyTenMinutes(); // كل 10 دقائق $schedule->command('command')->everyFifteenMinutes(); // كل 15 دقيقة $schedule->command('command')->everyThirtyMinutes(); // كل 30 دقيقة // جداول ساعية $schedule->command('command')->hourly(); // كل ساعة $schedule->command('command')->hourlyAt(17); // كل ساعة في الدقيقة 17 $schedule->command('command')->everyTwoHours(); // كل ساعتين $schedule->command('command')->everyThreeHours(); // كل 3 ساعات $schedule->command('command')->everyFourHours(); // كل 4 ساعات $schedule->command('command')->everySixHours(); // كل 6 ساعات // جداول يومية $schedule->command('command')->daily(); // يوميًا في منتصف الليل $schedule->command('command')->dailyAt('13:00'); // يوميًا في الساعة 1:00 مساءً $schedule->command('command')->twiceDaily(1, 13); // يوميًا في 1:00 صباحًا و 1:00 مساءً $schedule->command('command')->twiceDailyAt(1, 13, 15); // يوميًا في 1:15 صباحًا و 1:15 مساءً // جداول أسبوعية $schedule->command('command')->weekly(); // أسبوعيًا يوم الأحد في منتصف الليل $schedule->command('command')->weeklyOn(1, '8:00'); // أسبوعيًا يوم الاثنين في الساعة 8:00 صباحًا // جداول شهرية $schedule->command('command')->monthly(); // شهريًا في اليوم الأول في منتصف الليل $schedule->command('command')->monthlyOn(4, '15:00'); // شهريًا في اليوم الرابع في الساعة 3:00 مساءً $schedule->command('command')->twiceMonthly(1, 16); // شهريًا في اليوم 1 و 16 في منتصف الليل $schedule->command('command')->lastDayOfMonth('15:00'); // آخر يوم من الشهر في الساعة 3:00 مساءً // ربع سنوية وسنوية $schedule->command('command')->quarterly(); // ربع سنويًا في اليوم الأول في منتصف الليل $schedule->command('command')->yearly(); // سنويًا في 1 يناير في منتصف الليل $schedule->command('command')->yearlyOn(6, 1, '17:00'); // سنويًا في 1 يونيو في الساعة 5:00 مساءً // جداول أيام الأسبوع $schedule->command('command')->weekdays(); // من الاثنين إلى الجمعة في منتصف الليل $schedule->command('command')->weekends(); // السبت والأحد في منتصف الليل // أيام محددة $schedule->command('command')->mondays(); // كل يوم اثنين في منتصف الليل $schedule->command('command')->tuesdays(); // كل يوم ثلاثاء في منتصف الليل $schedule->command('command')->wednesdays(); // كل يوم أربعاء في منتصف الليل $schedule->command('command')->thursdays(); // كل يوم خميس في منتصف الليل $schedule->command('command')->fridays(); // كل يوم جمعة في منتصف الليل $schedule->command('command')->saturdays(); // كل يوم سبت في منتصف الليل $schedule->command('command')->sundays(); // كل يوم أحد في منتصف الليل

الجدولة الشرطية

قم بتشغيل المهام بناءً على الشروط باستخدام طرق when و skip:

// تشغيل عندما يكون الشرط صحيحًا $schedule->command('emails:send') ->daily() ->when(function () { return date('w') != 0; // لا تعمل يوم الأحد }); // تخطي عندما يكون الشرط صحيحًا $schedule->command('emails:send') ->daily() ->skip(function () { return DB::table('holidays') ->whereDate('date', today()) ->exists(); }); // بين نطاقات الوقت $schedule->command('command') ->hourly() ->between('8:00', '17:00'); // خارج نطاقات الوقت $schedule->command('command') ->hourly() ->unlessBetween('23:00', '4:00');

إخراج المهمة والتسجيل

التحكم في مكان إرسال إخراج المهمة:

// إرسال الإخراج إلى ملف $schedule->command('emails:send') ->daily() ->sendOutputTo('/var/log/emails.log'); // إلحاق الإخراج بملف $schedule->command('emails:send') ->daily() ->appendOutputTo('/var/log/emails.log'); // إرسال الإخراج بالبريد الإلكتروني $schedule->command('report:generate') ->daily() ->emailOutputTo('admin@example.com'); // إرسال الإخراج بالبريد الإلكتروني فقط عند الفشل $schedule->command('report:generate') ->daily() ->emailOutputOnFailure('admin@example.com'); // ping URL قبل/بعد المهمة $schedule->command('emails:send') ->daily() ->pingBefore('https://example.com/before-hook') ->pingAfter('https://example.com/after-hook'); // ping عند النجاح/الفشل $schedule->command('emails:send') ->daily() ->pingBeforeIf($condition, 'https://example.com/before') ->thenPing('https://example.com/success') ->pingOnFailure('https://example.com/failure');
نصيحة: استخدم خدمات مثل Healthchecks.io أو Cronitor لمراقبة المهام المجدولة باستخدام طرق ping.

منع تداخل المهام

منع تداخل المهام إذا كان التنفيذ السابق لا يزال قيد التشغيل:

// منع التداخل باستخدام mutex $schedule->command('emails:send') ->everyFiveMinutes() ->withoutOverlapping(); // انتهاء تداخل مخصص (بالدقائق) $schedule->command('emails:send') ->everyFiveMinutes() ->withoutOverlapping(10); // انتهاء القفل بعد 10 دقائق // تخطي إذا كان لا يزال قيد التشغيل $schedule->command('process:data') ->hourly() ->withoutOverlapping() ->skip(function () { return $this->isStillRunning(); });

تشغيل المهام على خادم واحد

عند تشغيل خوادم متعددة، تأكد من تشغيل المهام على خادم واحد فقط:

// يتطلب تكوين الذاكرة المؤقتة $schedule->command('report:generate') ->daily() ->onOneServer(); // مع اسم mutex مخصص $schedule->command('report:generate') ->daily() ->onOneServer('report-generation-lock');
ملاحظة: تتطلب طريقة onOneServer() برنامج تشغيل ذاكرة مؤقتة يدعم الأقفال (Redis، Memcached، DynamoDB، أو قاعدة البيانات).

مهام الخلفية

قم بتشغيل المهام المجدولة في الخلفية لأداء أفضل:

// تشغيل في الخلفية $schedule->command('emails:send') ->everyFiveMinutes() ->runInBackground(); // يمكن تشغيل مهام خلفية متعددة في وقت واحد $schedule->command('analytics:generate') ->hourly() ->runInBackground(); $schedule->command('reports:generate') ->hourly() ->runInBackground();

وضع الصيانة

التحكم في سلوك المهمة أثناء وضع الصيانة:

// تشغيل حتى في وضع الصيانة $schedule->command('critical:task') ->hourly() ->evenInMaintenanceMode(); // تخطي أثناء وضع الصيانة (افتراضي) $schedule->command('non-critical:task') ->hourly();

خطافات قبل وبعد

تنفيذ الكود قبل وبعد المهام المجدولة:

$schedule->command('emails:send') ->daily() ->before(function () { // المهمة على وشك التنفيذ \Log::info('بدء مهمة إرسال البريد الإلكتروني'); }) ->after(function () { // تم تنفيذ المهمة \Log::info('اكتملت مهمة إرسال البريد الإلكتروني'); }); // عند callback النجاح $schedule->command('emails:send') ->daily() ->onSuccess(function () { \Log::info('تم إرسال رسائل البريد الإلكتروني بنجاح'); }); // عند callback الفشل $schedule->command('emails:send') ->daily() ->onFailure(function () { \Log::error('فشلت مهمة إرسال البريد الإلكتروني'); // إرسال تنبيه إلى المسؤول });

اختبار المهام المجدولة

اختبر المهام المجدولة يدويًا أو في اختبارات آلية:

// قائمة جميع المهام المجدولة php artisan schedule:list // تشغيل المهام المجدولة يدويًا php artisan schedule:run // اختبار مهمة محددة php artisan schedule:test --name="emails:send" // عرض تاريخ الاستحقاق التالي للمهام php artisan schedule:list // في الاختبارات use Illuminate\Support\Facades\Schedule; public function test_command_is_scheduled() { $this->artisan('schedule:list') ->expectsOutput('emails:send'); }
تمرين 1: أنشئ مهمة مجدولة تنشئ تقارير مبيعات يومية في الساعة 6:00 صباحًا، وترسلها عبر البريد الإلكتروني إلى المديرين، وتؤرشف التقارير الأقدم من 90 يومًا. نفذ معالجة صحيحة للأخطاء والإشعارات.
تمرين 2: ابنِ نظام تذكير بالاشتراك يرسل تذكيرات قبل 7 أيام و 3 أيام ويوم واحد من انتهاء الاشتراك. استخدم الجدولة الشرطية لتخطي العطلات وعطلات نهاية الأسبوع.
تمرين 3: أنشئ مجدول نسخ احتياطي لقاعدة البيانات يعمل كل ساعة خلال ساعات العمل (8 صباحًا - 6 مساءً)، يرفع النسخ الاحتياطية إلى S3، ويحذف النسخ الاحتياطية المحلية الأقدم من 24 ساعة. نفذ pings فحص الصحة وإشعارات الفشل.

أنماط الجدولة الشائعة

// تنظيف قاعدة البيانات $schedule->command('db:cleanup') ->daily() ->at('03:00') ->onOneServer() ->withoutOverlapping(); // تسخين الذاكرة المؤقتة $schedule->call(function () { Cache::remember('popular_products', 3600, function () { return Product::popular()->get(); }); })->hourly(); // إنشاء خريطة الموقع $schedule->command('sitemap:generate') ->weekly() ->sundays() ->at('01:00') ->runInBackground(); // إرسال رسائل بريد إلكتروني موجزة $schedule->job(new SendWeeklyDigest) ->weekly() ->fridays() ->at('09:00'); // مراقبة صحة التطبيق $schedule->call(function () { Http::post('https://beats.envoyer.io/heartbeat/...', [ 'status' => 'up', ]); })->everyFiveMinutes();

جدولة المهام هي ميزة قوية لأتمتة المهام المتكررة في تطبيقات Laravel الخاصة بك. في الدرس التالي، سنستكشف استراتيجيات التخزين المؤقت لتحسين أداء التطبيق.