زمانبندی وظایف در لاراول (Laravel) — راهنمای مقدماتی
در این مقاله به توضیح یکی از خصوصیات فریمورک وب لاراول میپردازیم. یعنی میخواهیم زمانبندی وظایف در لاراول را بررسی کنیم. در طی این راهنما به روش مدیریت وظایف زمانبندیشده در اپلیکیشن از سوی لاراول میپردازیم. به علاوه در انتها وظایف زمانبندیشده خاص خود را برای مقاصد آموزشی ایجاد میکنیم.
فریمورک لاراول به ما اجازه میدهد که وظایف زمانبندیشده را طوری ایجاد کنیم که هیچ نگرانی در مورد راهاندازی آن در سطح سیستمی نداشته باشیم. بدین ترتیب میتوان ساختار cron را برای راهاندازی وظایف زمانبندیشده کنار گذاشت، چون لاراول امکان تعریف کردن آنها به روشی کاربرپسند را فراهم ساخته است.
در ابتدای مقاله به توضیح روش مورد استفاده برای راهاندازی کارهای سنتی cron میپردازیم و سپس روش لاراول را برای رسیدن به این مقصود بررسی میکنیم. در نیمه دوم این مقاله نیز تلاش میکنیم چند وظیفه زمانبندیشده سفارشی ایجاد کنیم که موجب میشود بینشی عملی از این موضوع پیدا کنید.
تنظیمات سنتی برای وظایف زمانبندی شده
در زمان توسعه اپلیکیشن به طور روزمره با موقعیتهایی مواجه میشویم که نیازمند اجرای برخی اسکریپتهای خاص دستورها به صورت دورهای هستیم. اگر با سیستمهای یونیکسی کار میکنید احتمالاً میدانید که job cron این دستورها را میتوانند اجرا کنند. از سوی دیگر این موارد در سیستمهای مبتنی بر ویندوز به نام وظایف زمانبندی شده (scheduled tasks) شناخته میشوند.
در ادامه به بررسی یک مثال ساده از job cron مبتنی بر یونیکس میپردازیم:
*/5 * * * * /web/statistics.sh
این کد کاملاً ساده است. این کد فایل statistics.sh را هر 5 دقیقه یک بار اجرا میکند.
با این که این روش بسیار ساده است؛ اما در اغلب موارد با موقعیتهایی مواجه هستیم که نیازمند پیادهسازی کاربردهای بسیار پیچیدهتری هستیم. از سوی دیگر سیستمهای پیچیده نیازمند تعریف کردن چندین job cron است که در بازههای زمانی متفاوتی اجرا شوند.
در ادامه برخی از وظایف را مشاهده میکنید که یک وب اپلیکیشن پیچیده باید به صوت دورهای در بکاند خود اجرا کند:
- پاکسازی دادههای غیرضروری از پایگاه داده بکاند
- بهروزرسانی اندیسهای کش فراتاند جهت حفظ بهروزرسانی
- محاسبه آمارهای وبسایت
- ارسال ایمیلها
- پشتیبانگیری از اجزای مختلف وبسایت
- تولید گزارشها
- و موارد دیگر
بنابراین چنانکه میبینید کارهای زیادی وجود دارند که باید به صورت دورهای و همچنین در بازههای زمانی متفاوت اجرا شوند. اگر یک «سیستم ادمین» پارهوقت باشید، تعریف job cron برای این وظایف، بسیار ساده خواهد بود؛ اما در پارهای مواد توسعهدهندگان میخواهند که روش آسانتری نیز در اختیار داشته باشند.
خوشبختانه لاراول دارای API داخلی Task Scheduling است که امکان تعریف کردن وظایف زمانبندیشده را به روشی بسیار آسان فراهم میکند. در بخش بعدی در مورد مبانی زمانبندی وظایف در لاراول صحبت خواهیم کرد.
زمانبندی وظایف در لاراول
در بخش قبلی به معرفی روش سنتی راهاندازی cron job پرداختیم. در این بخش به بررسی جزییات API زمانبندی وظایف در لاراول میپردازیم.
پیش از آن که ادامه بدهیم باید این نکته مهم را یادآور شویم که ویژگی زمانبندی ارائه شده از سوی لاراول شبیه به هیچ ویژگی دیگر نیست و به صورت خودکار فراخوانی نخواهد شد. بنابراین اگر فکر میکنید که نیازی به هیچ چیز دیگر در سطح سیستم ندارید در این صورت باید در تفکرات خود تجدید نظر کنید.
در واقع نخستین چیزی که در صورت تمایل به استفاده از زمانبندی وظایف در لاراول باید انجام دهید این است که یک cron job راهاندازی کنید که هر دقیقه اجرا شود و دستور artisan نمایش یافته در قطعه کد زیر را اجرا کند:
* * * * * php /path-to-your-project/artisan schedule:run >> /dev/null 2>&1
دستور artisan فوق به فراخوانی ابزار زمانبندی لاراول میپردازد و آن نیز به نوبه خود همه cron job-های در انتظار که در اپلیکیشن تعریفشدهاند را اجرا میکند. البته ما هنوز باید به بررسی روش تعریف کردن وظایف زمانبندیشده در لاراول بپردازیم و این بحث بعدی است که مطرح میشود. با استفاده از متد schedule در کلاس App\Console\Kernel اقدام به تعریف کردن وظایف زمانبندیشده خاص اپلیکیشن میکند. بنابراین پیشتر رفته و محتوای فایل app/Console/Kernel.php را مورد بررسی قرار میدهیم:
1<?php namespace App\Console;
2use Illuminate\Console\Scheduling\Schedule;
3use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
4
5class Kernel extends ConsoleKernel {
6 /**
7 * The Artisan commands provided by your application.
8 *
9 * @var array
10 */
11 protected $commands = [
12 'App\Console\Commands\Inspire',
13 ];
14
15 /**
16 * Define the application's command schedule.
17 *
18 * @param \Illuminate\Console\Scheduling\Schedule $schedule
19 * @return void
20 */
21 protected function schedule(Schedule $schedule)
22 {
23 $schedule->command('inspire')->hourly();
24 }
25}
همان طور که میبینید کد اصلی خودش یک مثال مفید است. در مثال فوق لاراول دستور artisan با نام inspire را به صورت ساعتی اجرا میکند. آیا فکر نمیکنید ساختار آن در وهله نخست کاملاً گویا است؟
در واقع روشهای مختلفی وجود دارد که به لاراول اجازه میدهد تا به تعریف وظایف زمانبندیشده بپردازد. به علاوه موارد فراوانی از نمونههای زمانبندی داخلی وجود دارد که میتوان از میان آنها انتخاب کرد:
- هر دقیقه/ هر پنج دقیقه
- ساعتی/ روزانه/ هفتگی/ فصلی/ سالانه
- در زمان خاصی از روز
- و موارد دیگر
در حقیقت میتوان گفت لاراول چنان مجموعه کاملی از رویهها را طراحی کرده که حتی نیاز به دست زدن به Shell برای ایجاد cron job-های سفارشی نیز نداریم. مطمئناً شما مشتاق هستید که بدانید چگونه میتوانید وظایف زمانبندیشده سفارشی را پیادهسازی کنید و این همان نکتهای است که در ابتدای مقاله قولش را به شما داده بودیم.
ایجاد وظایف زمانبندی شده در لاراول
همان طور که گفتیم، روشهای مختلفی برای زمانبندی وظایف در لاراول وجود دارد. در ادامه هر یک از آنها را بررسی میکنیم تا با طرز کارشان آشنا شویم.
متد Closure/Callable
API زمانبندی متد call را ارائه کرده است که امکان اجرای تابع callable یا یک تابع closure را میدهد. در ادامه محتوای فایل app/Console/Kernel.php که دارای کد زیر است را میبینید:
1<?php
2namespace App\Console;
3
4use Illuminate\Support\Facades\DB;
5use Illuminate\Console\Scheduling\Schedule;
6use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
7
8class Kernel extends ConsoleKernel
9{
10 /**
11 * Define the application's command schedule.
12 *
13 * @param \Illuminate\Console\Scheduling\Schedule $schedule
14 * @return void
15 */
16 protected function schedule(Schedule $schedule)
17 {
18 // the call method
19 $schedule->call(function () {
20 $posts = DB::table('posts')
21 ->select('user_id', DB::raw('count(*) as total_posts'))
22 ->groupBy('user_id')
23 ->get();
24
25 foreach($posts as $post)
26 {
27 DB::table('users_statistics')
28 ->where('user_id', $post->user_id)
29 ->update(['total_posts' => $post->total_posts]);
30 }
31 })->everyThirtyMinutes();
32 }
33}
همان طور که میبینید ما تابع closure را به عنوان اولین آرگومان متد call ارسال کردهایم. ضمناً فراوانی آن را به صورت هر 30 دقیقه یک بار تنظیم کردهایم، بنابراین تابع closure هر 30 دقیقه یک بار اجرا میشود. در مثال فوق ما تعداد نوشتههای هر کاربر را شمارش کرده و جدول آمار را بر همین اساس بهروزرسانی میکنیم.
دستور Artisan
علاوه بر closure-ها یا callable-ها میتوانید یک دستور artisan را نیز طوری زمانبندی کنید که در بازههای معینی اجرا شود. در واقع این دستور باید رویکرد ترجیحی نسبت به closure-ها باشد، زیرا بهینهسازی کد بیشتری دارد و همزمان به افزایش قابلیت استفاده مجدد از کد نیز منتهی میشود.
بدین ترتیب در ادامه به بررسی محتوای فایل app/Console/Kernel.php با کد زیر میپردازیم:
1<?php
2namespace App\Console;
3
4use Illuminate\Support\Facades\Config;
5use Illuminate\Console\Scheduling\Schedule;
6use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
7
8class Kernel extends ConsoleKernel
9{
10 /**
11 * The Artisan commands provided by your application.
12 *
13 * @var array
14 */
15 protected $commands = [
16 'App\Console\Commands\UserStatistics'
17 ];
18
19 /**
20 * Define the application's command schedule.
21 *
22 * @param \Illuminate\Console\Scheduling\Schedule $schedule
23 * @return void
24 */
25 protected function schedule(Schedule $schedule)
26 {
27 // artisan command method
28 $schedule->command('statistics:user')->everyThirtyMinutes();
29 }
30
31 /**
32 * Register the Closure based commands for the application.
33 *
34 * @return void
35 */
36 protected function commands()
37 {
38 require base_path('routes/console.php');
39 }
40}
این همان متد command است که میتوانید به صورت یک دستور artisan چنان که در کد فوق میبینید مورد استفاده قرار دهید. در این مورد باید امضای دستور artisan را به عنوان آرگومان نخست متد command ارسال کنید. البته باید دستور artisan متناظر را نیز در فایل app/Console/Commands/UserStatistics.php تعریف کنید:
1<?php
2namespace App\Console\Commands;
3
4use Illuminate\Console\Command;
5use Illuminate\Support\Facades\DB;
6
7class UserStatistics extends Command
8{
9 /**
10 * The name and signature of the console command.
11 *
12 * @var string
13 */
14 protected $signature = 'statistics:user';
15
16 /**
17 * The console command description.
18 *
19 * @var string
20 */
21 protected $description = 'Update user statistics';
22
23 /**
24 * Create a new command instance.
25 *
26 * @return void
27 */
28 public function __construct()
29 {
30 parent::__construct();
31 }
32
33 /**
34 * Execute the console command.
35 *
36 * @return mixed
37 */
38 public function handle()
39 {
40 // calculate new statistics
41 $posts = DB::table('posts')
42 ->select('user_id', DB::raw('count(*) as total_posts'))
43 ->groupBy('user_id')
44 ->get();
45
46 // update statistics table
47 foreach($posts as $post)
48 {
49 DB::table('users_statistics')
50 ->where('user_id', $post->user_id)
51 ->update(['total_posts' => $post->total_posts]);
52 }
53 }
54}
دستور Exec
میتوان گفت متدهایی که تا به اینجا برسی کردیم مختص خود اپلیکیشن لاراول بودند. علاوه بر اینها لاراول میتواند دستورهای shell را نیز که در خارج از اپلیکیشن اجرا میشوند زمانبندی کند. در ادامه مثال سادهای از این وضعیت را میبینید که روش پشتیبانگیری از پایگاه داده به صورت روزانه را نمایش میدهد:
1<?php
2namespace App\Console;
3
4use Illuminate\Console\Scheduling\Schedule;
5use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
6
7class Kernel extends ConsoleKernel
8{
9 /**
10 * Define the application's command schedule.
11 *
12 * @param \Illuminate\Console\Scheduling\Schedule $schedule
13 * @return void
14 */
15 protected function schedule(Schedule $schedule)
16 {
17 // exec method
18 $host = config('database.connections.mysql.host');
19 $username = config('database.connections.mysql.username');
20 $password = config('database.connections.mysql.password');
21 $database = config('database.connections.mysql.database');
22
23 $schedule->exec("mysqldump -h {$host} -u {$username} -p{$password} {$database}")
24 ->daily()
25 ->sendOutputTo('/backups/daily_backup.sql');
26 }
27}
از کد فوق مشخص است که باید از متد exec ابزار زمانبندی استفاده کنید و باید دستوری را که میخواهید اجرا شود به عنوان آرگومان نخست ارسال کنید. علاوه بر آن ما باید از متد sendOutputTo نیز استفاده کنیم که امکان گردآوری خروجی دستور را به ما میدهد. از سوی دیگر متدی به نام emailOutputTo نیز وجود دارد که محتوای خروجی را به یک آدرس تعیین شده ایمیل میکند. بدین ترتیب به پایان این مقاله میرسیم. در حقیقت در این نوشته ما تنها گوشه کوچکی از API زمانبندی لاراول را مورد بررسی قرار دادیم و موارد بسیار دیگری هستند که باید مورد بررسی قرار گیرند.
سخن پایانی
در این مقاله به بررسی API زمانبندی فریمورک وب لاراول پرداختیم. مشاهده میزان سادگی مدیریت وظایفی که قرار است به صورت دورهای اجرا شوند بسیار جذاب است. در ابتدای مقاله به بررسی روش سنتی راهاندازی وظایف زمانبندی پرداختیم و در ادامه روش لاراول برای انجام این کار را معرفی کردیم. در نیمه دوم مقاله نیز چندین مثال عملی برای نمایش مفاهیم زمانبندی وظیفه ارائه کردیم. امیدواریم از این نوشته بهره برده باید و اینک اعتمادبهنفس بیشتری در خصوص راهاندازی وظایف زمانبندیشده در لاراول داشته باشید.
اگر این مطلب برای شما مفید بوده است، آموزشهای زیر نیز به شما پیشنهاد میشوند:
- مجموعه آموزش های برنامه نویسی PHP
- آموزش REST API در Laravel (لاراول) با بسته Passport
- مجموعه آموزشهای برنامهنویسی
- آموزش تحلیل یک سیستم فروشگاهی در فریمورک لاراول (Laravel)
- آموزش پیاده سازی سیستم Logging در فریمورک لاراول (Laravel)
- برنامهنویسی PHP و هر آنچه برای شروع باید بدانید — آموزش جامع
- بهینه سازی عملکرد اپلیکیشنهای PHP — به زبان ساده
==