در بخش سوم از صف بندی و آموزش ساخت Queue در PHP دیدید که چطور یک task را صف بندی کردیم. تنها چیزی که مانده بود این است که تسک مورد نظر در صف که ارسال ایمیل بود را پردازش کنیم تا کار اصلی خود (ارسال ایمیل) را انجام دهد.
مزیت صف بندی task پردازش صف بندی
حالا شاید کنجکاو باشید که صف بندی در php چه مزیت هایی جدا از افزایش سرعت پاسخ در اپلیکشن دارد :
- شما می توانید آمار پردازش (process) را ببینید : چه مقدار موفق (succeed) یا ناموفق (fail) بودند
- شما می توانید زمان دقیق task را بطور میانگین ببینید
- می توانید خطاهای پردازشی را دنبال کنید, دلیلش رو پیدا کنید.این مورد بسیار مفید است اگر هم تیمی شما پسور یک سرویس وابسته به task شما را تغییر بده, یا اعتبار شما تمام شده و نیاز به شارژ حساب دارید.
- بعضی از وظایف پیچیده را می توانید پردازش شوند, برای مثال, اگر ایمیل های صف بندی شده را توسط سرور gmail ارسال کنید, به شما اجازه ارسال تنها ۲۰۰۰ تا در روز را می دهد, بنابراین استفاده از صف بندی می تواند کار را آسان کند
- پردازش می تواند بهترین عملکرد را داشته باشد اگر فقط در بهترین زمان سرور خود باشید, برای مثال, زمانی که شب است یا کانکشن اینترنت یا سیگنال موبایل وجود داشته باشد.
- می توانید وظایف یا همان task ها را بصورت دسته ای process کنید, برای مثال, یک کانکشن تنها متصل به STMP سرور ایجاد و صدهای ایمیل صف بندی شده را در یک سشن (session) ارسال کنید.
پردازش یک صف
اگر فیلم “The Imitation Game” را داده باشید, به خاطر دارید چند worker خاص وجود داشت (گروهی از خانم ها) که به هر کدام میزی برای دیکد کاغذها داده شده بود. و هر چه زودتر دیکد میشد به مدیر آنجا پس می دادند.
این یک مثال عالی از یک worker (ترجمه فارسی : کارگر) یا “پردازشگر صف” است.
بنابراین در بخش چهارم مقاله آموزش ساخت Queue در PHP , قصد داریم به شما نحوه ساخت یک پردازشگر صف (queue processor) را نشان بدیم.
این پردازشگر صف یا کوتاه تر worker ,یک اسکریپت ساده است که :
- بطور عادی اجرا می شود, معمولا به عنوان یک برنامه cron و فرضا هر ۵ دقیقه
- تعدادی (n تا) کار (job) را از صف (queue) را دریافت می کند
- Job های روی میز را یک به یک پردازش می کند.
- نتایج را گزارش می دهد: job را به عنوان “پردازش شده” ثبت می کند و یا پیام خطا را لاگ می گیرید تا بعدا بتوانید آن را بررسی کنید.
بنابراین یک اسکریپت worker ساده بصورت زیر است :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
<?php class Worker { protected $_queue; /** * remember the queue reference * @param object $queue */ public function __construct($queue) { $this->_queue = $queue; } public function process() { $item = $this->_queue->getItem(); manipulate_the_item_to_do_the_work($item); return true; } }; $worker->process(); |
این ساده ترین مثال از کد است, که یک job را در هر بار اجرا پردازش می کتد و قدم چهارم هم نتایج را گزارش می کند که هنوز پیاده سازی نکردیم.
به هر حال, این کد php ساده می تواند هر ۵ دقیقه اجرا شود البته اگر در تنظیمات crontab اضافه کنید. کافیست کد بالا را به عنوان فایل php ذخیره کنید و به صورت زیر اضافه کنید (آموزش cronjob)
1 2 |
> crontab -e //run this to open crontab settings */5 * * * * php /path/queue_worker.php |
مثال واقعی – ساخت Queue در PHP
حالا قصد داریم یک مثال واقعی وب را پیاده سازی کنیم. آیتم های صف بندی شده ما همان ارسال ایمیل به کاربر بعد از عضویت موفق یا خرید و غیره است.
بنابراین با کد sql زیر یک جدول در دیتابیس ایجاد می کنیم :
1 2 3 4 5 6 7 8 9 10 11 |
CREATE TABLE IF NOT EXISTS `queue` ( `id` int(10) NOT NULL PRIMARY KEY AUTO_INCREMENT, `email` varchar(100) NOT NULL, `html` text NOT NULL, `status` enum('queued','processing','done','failed') NOT NULL DEFAULT 'queued', `error_text` varchar(255) DEFAULT NULL, `created_at` datetime DEFAULT NULL, `updated_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00' ON UPDATE CURRENT_TIMESTAMP ) ENGINE=InnoDB DEFAULT CHARSET=utf8; |
نکته :
- فیلد
status
معادل “queued
” که پیش فرض است – به معنی این است که job صف بندی شده و در انتظار پردازش است.
زمانی که ما یک المنت را از صف برای پردازش می گیریم, مقدار فیلد را به “processing
” تغییر می دیم.
اگر پردازش موفق بود, مقدار را به “done
” آپدیت می کنیم, در غیر اینصورت, “failed
” و پیام خطا را لاگ می گیریم.
- بنابر تجربه من, بهتر است که زمان را بصورت
timestamp
لاگ بگیرید زمانی که آیتم صف بندی شد و یا وضعیت آن تغییر کرد. این مقدار در فیلد هایcreated_at
وupdated_at
ذخیره می شود.
زمانی که ما یک رکورد جدید در جدول ایجاد می کنیم, مقدار created_at
را برار زمان فعلی قرار میدیم و هر بار که آپدیت می شود (مثلا فیلد status
), مقدار فیلد updated_at
بصورت خودکار آپدیت می شود.
این یک ترفند MySQL برای فیلد های TIMESTAMP
است که تنظیمات “ON UPDATE CURRENT_TIMESTAMP
” دارند.
صف بندی task ها
ما یک جدول برای صف بندی job های خودمان داریم. حالا کد افزودن آیتم به صف را می نویسیم.
این کاملا بستگی به نحوه پیاده سازی و یا فریمورک ما دارد, اما اینجا فقط نیاز است که آبجکت کانکشن دیتابیس خود را به متد constructor
پاس بدید.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
?php class Queue { protected $_databaseHandler; /** * remember the database reference * @param object $databaseHandler */ public function __construct($databaseHandler) { $this->_databaseHandler = $databaseHandler; } /** * runs an INSERT query to append a new task to the existing queue table * @param string $emailAddress * @param string $html */ public function addItem($emailAddress, $html) { $this->_databaseHandler->query('INSERT INTO queue (`id`, `email`, `html`, `created_at`, `updated_at`) VALUES (NULL, "' . quote($emailAddress). '", "' . quote($html). '", NOW(), NOW())'; return true; } /** * requests a new queued item (not being processed, done or failed. A new one!) * @return object $item */ public function getItem() { $item = $this->_databaseHandler->query('SELECT * FROM queue WHERE status="queued" LIMIT 1'); return $item; } }; ?> |
Worker پردازشگر صف
حالا نیاز داریم که worker را بنویسیم که در قالب یک کلاس ایجاد کردیم و همچنین نیاز است که هندلر دیتابیس را نیز قرار بدیم.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 |
<?php class Worker { protected $_databaseHandler; protected $_queue; /** * remember the references * @param object $queue * @param object $databaseHandler */ public function __construct($queue, $databaseHandler) { $this->_queue = $queue; $this->_databaseHandler = $databaseHandler; } public function process() { $item = $this->_queue->getItem(); $this->markItemAsBeingProcessed($item); try { mail( $item['email'], 'Your are subscribed!', $item['html'] ); // if mail() was ok, just mark the item as done $this->markItemAsDone($item); } catch (Exception $e) { //if mail() fails, mark item as failed and save the error text $errorMessageText = $e->getMessage(); $this->markItemAsFailed($item, $errorMessageText); } } public function markItemAsBeingProcessed($item) { $this->_changeItemStatus($item['id'], 'processing'); } public function markItemAsDone($item) { $this->_changeItemStatus($item['id'], 'done'); } public function markItemAsFailed($item, $errorMessageText) { $this->_changeItemStatus($item['id'], 'failed', $errorMessageText); } protected function _changeItemStatus($itemId, $itemStatus, $errorMessageText=null) { $this->_databaseHandler->query('UPDATE queue SET `status` = '. $itemStatus . ', `error_text` = '. $errorMessageText . ' WHERE `id` = '. $itemId; } }; ?> |
خب تمام شد بخش چهارم آموزش ساخت Queue در PHP همین قدر بود !
بهبود عملکرد پردازش صف
راه های کمی برای بهبود این سیستم وجود دارد. بطور مثال اگر نیاز به اجرای بیشتر از یک آیتم را دارید مثلا ۱۰ یا ۲۰ تا, در این مورد نیاز است که :
متد Queue::getitem()
را تغییر بدید, یک پارامتر $number
به عنوان job های ضروری برای پردازش پاس بدید و مقدار “LIMIT 1
” را به “LIMIT $number
” در کویری SQL تغییر بدید
جمع بندی
همانطور که دیدید پیاده سازی و ساخت یک صف برای پردازش کار بسیار پیچیده و عجیب و غریبی نیست و حتی داخل فریمورک ها اینکار بی نهایت آسان است.
شما نیاز بود که فقط یکبار آن را پیاده سازی کنید اما به قول معروف به شما امکان یک تیر دو نشان (البته خیلی بیشتر از دو) را می دهد.
شاید در سایت های کم بازدید این امر آنقدر ها هم مهم نباشد ولی در پروژه های کمی سنگین مثل وب سایت های دیجی کالا و آپارات و غیره این امر آنقدر اهمیت دارد که هزینه های زیادی برای پیاده سازی و کنترل این آیتم ها داده می شود.
امیدوارم از آموزش ساخت Queue در PHP نهایت استفاده را برده باشید .
هر سوالی داشتید ، از قسمت نظرات ارسال کنید . سریعا ، پاسخگوی سوالات شما هستیم .
موفق و پیروز باشید.