RabbitMQ × Magentoمن الصفر لموديول شغّال.
هنفهم الـ Message Queue في Magento من الأساس، وبعدين نبني module شغّال خطوة بخطوة: publisher بيبعت رسالة، وconsumer بياخدها ويعالجها في الخلفية. كل ملف مشروح ودوره واضح.
Magento 2 بيتعامل مع RabbitMQ عن طريق Message Queue Framework: بتعرّف الـ topic في communication.xml، والـ exchange والـ queue في queue_topology.xml، والـ consumer في queue_consumer.xml، والـ publisher في queue_publisher.xml. الـ publisher class بيبعت الرسالة، والـ consumer بيعالجها في الخلفية، وبتشغّله بأمر bin/magento queue:consumers:start.
المفاهيم الأساسية
المصطلحات اللازمة قبل ما نبني.
- Publisher — الجزء اللي بيبعت الرسالة (بيحطها في النظام).
- Exchange — "موزّع البريد" — بياخد الرسالة ويقرّر تروح لأنهي queue حسب قواعد (routing).
- Queue — الطابور اللي بتستنى فيه الرسائل لحد ما تتعالج.
- Consumer — الجزء اللي بياخد الرسائل من الـ queue وينفّذها.
- Topic — اسم/عنوان الرسالة، بيربط الـ publisher بالـ consumer الصح.
MQF في Magento
إزاي Magento بيغلّف RabbitMQ.
الـ MQF (Message Queue Framework) هو طبقة في Magento بتغلّف RabbitMQ. بدل ما تتعامل مع RabbitMQ مباشرة، بتعرّف كل حاجة في ملفات XML، وMagento بيتولّى الباقي.
عشان يفصل الإعداد (فين الرسالة رايحة) عن الكود (إيه اللي بيتعمل). ده بيخلّي النظام مرن — تقدر تغيّر التوجيه من غير ما تلمس الكود.
خريطة الملفات
الملفات اللي هنعملها ودور كل واحد. الصورة الكاملة قبل ما نبدأ.
خطوة 1: الموديول
نبدأ بالملفين الأساسيين لأي موديول.
use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register( ComponentRegistrar::MODULE, 'Vendor_QueueDemo', __DIR__ );
<config> <module name="Vendor_QueueDemo"> <sequence> <module name="Magento_Sales"/> </sequence> </module> </config>
خطوة 2: communication.xml
نعرّف الـ topic — اسم الرسالة ونوع بياناتها.
بيعرّف الـ topic (عنوان الرسالة) ونوع البيانات اللي بتتبعت معاها (request). ده العقد اللي الـ publisher والـ consumer بيتفقوا عليه.
<config> <topic name="vendor.queuedemo.process" request="string" handler="Vendor\QueueDemo\Model\Consumer::process"/> </config>
- name — اسم الـ topic. هنستخدمه في كل الملفات التانية.
- request — نوع البيانات. هنا string (بسيط). ممكن يكون interface لبيانات معقّدة.
- handler — الـ method اللي هتعالج الرسالة (في الـ consumer).
خطوة 3: queue_topology.xml
نعرّف الـ exchange وإزاي بيوجّه الرسالة للـ queue.
بيعرّف الـ exchange (الموزّع) والـ binding — القاعدة اللي بتربط الـ topic بالـ queue. يعني "الرسالة بتاعة topic كذا، تروح queue كذا".
<config> <exchange name="magento-demo" type="topic" connection="amqp"> <binding id="demoBinding" topic="vendor.queuedemo.process" destinationType="queue" destination="vendor.queuedemo.queue"/> </exchange> </config>
- exchange name — اسم الموزّع.
- connection="amqp" — يعني RabbitMQ (amqp البروتوكول). لو db يبقى MySQL.
- binding — بيربط الـ topic بالـ queue المحدّدة في destination.
خطوة 4: queue_consumer.xml
نربط الـ queue بالـ consumer class.
بيعرّف الـ consumer — بيقول "الـ queue دي، الكلاس ده هو اللي بيعالجها". وبيدّي الـ consumer اسم نستخدمه في التشغيل.
<config> <consumer name="vendor.queuedemo.consumer" queue="vendor.queuedemo.queue" connection="amqp" handler="Vendor\QueueDemo\Model\Consumer::process"/> </config>
- name — اسم الـ consumer (هنستخدمه في الـ CLI للتشغيل).
- queue — الـ queue اللي بيسمع منها (نفس اللي في topology).
- handler — الكلاس والـ method اللي بتعالج.
خطوة 5: queue_publisher.xml
نعرّف إزاي الـ topic بيتنشر.
بيقول "الـ topic ده بيتنشر على أنهي exchange وconnection". بيكمّل الدايرة — بيربط الـ topic بالـ exchange اللي عرّفناه في topology.
<config> <publisher topic="vendor.queuedemo.process"> <connection name="amqp" exchange="magento-demo"/> </publisher> </config>
خطوة 6: Publisher class
الكود اللي بيبعت الرسالة فعليًا.
بيستخدم الـ PublisherInterface عشان يبعت رسالة للـ topic. أي حد عايز يبعت، بينده الكلاس ده.
namespace Vendor\QueueDemo\Model; use Magento\Framework\MessageQueue\PublisherInterface; class Publisher { private const TOPIC = 'vendor.queuedemo.process'; public function __construct( private readonly PublisherInterface $publisher ) {} public function execute(string $message): void { // send the message to the queue $this->publisher->publish( self::TOPIC, $message ); } }
خطوة 7: Consumer class
الكود اللي بيستقبل الرسالة ويعالجها.
فيه الـ process() method (اللي عرّفناها كـ handler). دي بتشتغل لما رسالة توصل. هنا بنكتب المنطق — في المثال، نسجّل في log.
namespace Vendor\QueueDemo\Model; use Psr\Log\LoggerInterface; class Consumer { public function __construct( private readonly LoggerInterface $logger ) {} // runs when a message arrives public function process(string $message): void { // your logic here (e.g. send to ERP) $this->logger->info( 'QueueDemo received: ' . $message ); } }
خطوة 8: التشغيل والاختبار
نفعّل الموديول ونشغّل الـ consumer ونجرّب.
# 1. فعّل الموديول bin/magento module:enable Vendor_QueueDemo bin/magento setup:upgrade bin/magento cache:clean # 2. شغّل الـ consumer (بيسمع للرسائل) bin/magento queue:consumers:start \ vendor.queuedemo.consumer # للاختبار: شغّله لعدد محدود من الرسائل bin/magento queue:consumers:start \ vendor.queuedemo.consumer --max-messages=10
تنده الـ Publisher من أي مكان — observer، controller، أو command. مثلًا observer على sales_order_place_after بينده $publisher->execute('Order #123 placed').
بعد ما تبعت رسالة والـ consumer شغّال، تشوف الرسالة في الـ log (var/log/). لو ظهرت "QueueDemo received..." — تمام، الدايرة كاملة اشتغلت.
- الـ Publisher.php بينده publish(topic, msg).
- الـ publisher.xml بيوجّه الـ topic للـ exchange.
- الـ topology.xml (exchange) بيوجّهه للـ queue.
- الرسالة بتستنى في الـ queue.
- الـ consumer.xml بيقول مين يعالج الـ queue دي.
- الـ Consumer.php بياخد الرسالة ويعالجها في الخلفية.
موديول RabbitMQ شغّال ✓
دلوقتي عندك module كامل: publisher بيبعت، 4 ملفات XML بيوجّهوا، وconsumer بيعالج في الخلفية. فهمت المفاهيم والبناء والتشغيل من الصفر.
الخلاصة: الـ topic بيربط الكل — من communication للـ publisher للـ exchange للـ queue للـ consumer. دي الدايرة اللي أي message queue في Magento بيمشي عليها. الباقي مجرد منطق أعقد جوّه الـ consumer.
خلّصت الدرس؟علّمه عشان تتابع تقدّمك في الكورس.
أسئلة شائعة
إزاي أستخدم RabbitMQ في Magento 2؟
Magento بيغلّف RabbitMQ بطبقة اسمها Message Queue Framework (MQF)، فبدل ما تتعامل معاه مباشرة بتعرّف كل حاجة في ملفات XML جوّه etc/. بتكتب publisher class بينادي publish() على topic، وconsumer class بيعالج الرسالة، وبتشغّل الـ consumer بـ bin/magento queue:consumers:start.
ما الفرق بين communication.xml و queue_topology.xml و queue_consumer.xml و queue_publisher.xml؟
الـ communication.xml بيعرّف الـ topic ونوع البيانات اللي بتتبعت، والـ queue_topology.xml بيعرّف الـ exchange والـ binding اللي بيوجّه الـ topic لـ queue معيّنة. الـ queue_consumer.xml بيربط الـ queue بالكلاس اللي هيعالجها، أما queue_publisher.xml فبيحدد الـ topic بيتنشر على أنهي exchange وconnection.
يعني إيه Publisher و Exchange و Queue و Consumer في RabbitMQ؟
الـ Publisher بيبعت الرسالة، والـ Exchange بياخدها ويقرّر تروح لأنهي queue حسب قواعد الـ routing، والـ Queue هي الطابور اللي الرسالة بتستنى فيه، والـ Consumer بياخدها وينفّذها. والـ Topic هو اسم الرسالة اللي بيربط الكل. الميزة إن الـ publisher والـ consumer مايعرفوش بعض، وده الـ decoupling اللي بيخلّي الشغل يتم في الخلفية.
إزاي أشغّل consumer في Magento 2؟
بتكتب bin/magento queue:consumers:start وبعده اسم الـ consumer المعرّف في queue_consumer.xml، ولو بتجرّب ضيف --max-messages=10 عشان يعالج عدد محدود. في production الـ consumers لازم تفضل شغّالة دايمًا عن طريق cron أو supervisor، ولو غيّرت ملفات الـ XML اعمل setup:upgrade وcache:clean وأعد تشغيل الـ consumer.
هل ينفع أستخدم message queue في Magento 2 من غير RabbitMQ؟
أيوه، Magento يقدر يستخدم MySQL كـ queue، وده بيتحدد بالـ connection: amqp يعني RabbitMQ وdb يعني MySQL. بس الـ MySQL أبطأ، وفي production الجدّي RabbitMQ هو الاختيار الأفضل، وإعداده بيتحط في env.php.
إزاي أبعت بيانات الأوردر لـ queue لما order يتعمل في Magento 2؟
اعمل observer على event sales_order_place_after وخليه ينادي الـ Publisher class بتاعك، اللي بيستخدم PublisherInterface::publish() بالـ topic والرسالة. الـ publisher مبيستناش نتيجة، والـ consumer هو اللي بيعالج الرسالة في الخلفية، زي إنه يسجّلها في log أو يبعتها لـ ERP.