كيفية توسيع نطاق الأنظمة عبر تقسيم العمليات واستخدام Redis
مقدمة: لماذا نلجأ إلى تقسيم العمليات في الأنظمة الكبيرة؟
عند بناء الأنظمة الموزعة، قد نصل إلى مرحلة تصبح فيها رسالة واحدة أو مهمة واحدة كبيرة جداً إلى حد يصعب معه تمريرها أو معالجتها ضمن عملية واحدة فقط. في هذه الحالات، لا تكون المشكلة دائماً في منطق العمل نفسه، بل في حجم الرسالة، وحدود البنية التحتية، وطريقة تنسيق التنفيذ بين الخدمات المختلفة.
الفكرة الأساسية هنا هي تحويل المهمة الكبيرة إلى أجزاء أصغر يمكن التعامل معها بشكل متوازٍ وأكثر مرونة. هذا النمط المعماري لا يساعد فقط على تجاوز قيود الحجم، بل يفتح الباب أيضاً أمام تحسين القابلية للتوسع ورفع كفاءة المعالجة.

في هذا المقال، سنستعرض نمطاً عملياً لتوسيع النظام باستخدام Process Splitting مع Redis، مع التركيز على الفكرة المعمارية بدلاً من تفاصيل التنفيذ الدقيقة.
المشكلة الأساسية: عندما تصبح الرسالة أكبر من قدرة النظام
لنفترض وجود خدمتين منفصلتين: الخدمة Service A والخدمة Service B، وبينهما وسيط رسائل يعمل بأسلوب Pub/Sub. تقوم الخدمة الأولى بنشر رسالة، ثم يتكفل الوسيط بتمريرها إلى الخدمة الثانية كي تبدأ المعالجة.
بعد انتهاء المعالجة، يتم عادة تسجيل أن المهمة اكتملت بنجاح. يبدو هذا التدفق بسيطاً ومباشراً، لكن المشكلة تبدأ عندما تكون الرسالة نفسها كبيرة جداً بحيث تتجاوز الحدود التي يسمح بها وسيط الرسائل.
بمعنى آخر، قد يكون النظام سليماً منطقياً، لكن الرسالة تفشل في النشر بسبب قيود الحجم في خدمة Pub/Sub. وهذه مشكلة شائعة في الأنظمة المعتمدة على الرسائل، خصوصاً عندما تحتوي الرسائل على قوائم كبيرة أو بيانات مركبة.
أسباب شائعة لظهور هذه المشكلة
- ازدياد حجم البيانات المرسلة داخل الرسالة الواحدة.
- وجود حدود ثابتة في خدمة الوساطة أو النقل.
- الاعتماد على رسالة ضخمة بدلاً من توزيع الحمل على عدة رسائل أصغر.
- نمو النظام بمرور الوقت دون تعديل النمط المعماري الأساسي.
المقاربة الأولى: هل يكفي رفع الحد الأقصى لحجم الرسالة؟
أول حل قد يتبادر إلى الذهن هو ببساطة تعديل الإعدادات لزيادة الحجم الذي يمكن أن تستقبله خدمة Pub/Sub. من الناحية التقنية، قد يكون ذلك ممكناً أحياناً عبر تغيير إعداد واحد فقط.
لكن هذا الحل، رغم سهولته، ليس مناسباً على المدى الطويل. فإذا استمرت الرسائل في النمو، فهل سنواصل رفع الحد في كل مرة؟ هذا النهج يؤدي غالباً إلى مشاكل تتعلق بالتوسع، واستهلاك الموارد، وصعوبة صيانة النظام مستقبلاً.
لذلك، فإن زيادة السقف ليست حلاً جذرياً، بل مجرد تأجيل للمشكلة.
الحل العملي: تقسيم الرسالة الكبيرة إلى رسائل أصغر
الحل الأكثر مرونة هو تقسيم الرسالة الكبيرة إلى مجموعة من الرسائل الصغيرة، بحيث تمثل كل رسالة جزءاً مستقلاً من المهمة الكاملة. وبدلاً من معالجة كل شيء داخل عملية واحدة، يتم تنفيذ عدة عمليات متوازية.

إذا كانت الرسالة الأصلية تحتوي مثلاً على قائمة من العناصر، فمن المنطقي فصل كل عنصر في رسالة مستقلة. لنفترض أن لدينا 10 عناصر. في التصميم القديم كانت العناصر كلها تُرسل داخل رسالة واحدة، أما بعد التقسيم فستتحول إلى 10 رسائل مستقلة.
هذا يعني أن عملية نشر واحدة ستصبح عدة عمليات نشر، وأن مهمة واحدة ستتحول إلى عدة مهام متزامنة. قد يبدو ذلك أكثر تعقيداً من النظرة الأولى، لكنه في الواقع يمنح النظام قدرة أفضل على التوسع والتعامل مع الأحمال الكبيرة.

فوائد تقسيم العمليات
- تقليل خطر فشل النشر بسبب حجم الرسالة.
- توزيع الحمل على عدة عمليات بدلاً من عملية واحدة مرهقة.
- تحسين الاستفادة من المعالجة المتوازية.
- رفع قابلية النظام للتوسع الأفقي.
- منح كل جزء من البيانات فرصة للمعالجة المستقلة وإعادة المحاولة عند الحاجة.
متى يكون التقسيم مناسباً؟
يكون هذا النمط فعالاً عندما تكون الرسالة قابلة للتجزئة بشكل طبيعي، مثل:
- قائمة طلبات.
- مجموعة ملفات.
- دفعة سجلات
Records. - عناصر مستقلة لا تعتمد كلياً على بعضها أثناء التنفيذ.
التحدي الأهم بعد التقسيم: كيف نعرف أن جميع العمليات انتهت؟
رغم أن تقسيم الرسائل يحل مشكلة الحجم، إلا أنه يخلق تحدياً جديداً: كيف نتأكد أن المهمة الكاملة انتهت فعلاً؟
عندما تنفذ عدة عمليات بشكل متزامن، لن يكون من السهل معرفة اللحظة التي اكتملت فيها كل الأجزاء، إلا إذا كان لدينا أسلوب واضح لتتبع التقدم.
وهنا تظهر أهمية وجود عدّاد مركزي يمكن الاعتماد عليه.
استخدام Redis لتتبع اكتمال العمليات المتوازية
الحل العملي هنا هو تخزين عدد العمليات المطلوب إنجازها في البداية، ثم إنقاص هذا العدد كلما انتهت عملية فرعية. وعندما يصل العداد إلى 0، فهذا يعني أن كل الأجزاء اكتملت، ويمكن عندها تعليم المهمة الأصلية على أنها منتهية.
لتنفيذ هذا النمط نحتاج إلى مخزن سريع وموثوق للحالة المؤقتة، وهنا يأتي دور Redis. ورغم أن Redis يُستخدم غالباً كطبقة Cache، إلا أنه مناسب جداً أيضاً لحفظ العدادات والبيانات القصيرة التي تحتاج إلى تحديث سريع.
في هذا السيناريو، يمكن تهيئة العداد الأولي بناءً على عدد الأجزاء الناتجة من عملية التقسيم. فإذا كانت الرسالة تحتوي على قائمة، فغالباً سيكون طول القائمة هو القيمة الابتدائية المناسبة.
بعد ذلك:
- نحسب عدد الأجزاء قبل النشر.
- نخزن هذا العدد في
Redis. - ننشر كل جزء كرسالة مستقلة.
- كلما أنهت عملية فرعية عملها، يتم تنفيذ
decrementللعداد. - إذا أصبح العداد يساوي
0، نعتبر أن المهمة الأصلية اكتملت بالكامل.

لماذا يعد Redis مناسباً لهذا النمط؟
- سريع جداً في عمليات القراءة والكتابة.
- يدعم العدادات بشكل عملي وفعال.
- مناسب لتخزين الحالة المؤقتة بين الخدمات.
- يلائم بيئات الأنظمة الموزعة التي تحتاج إلى تنسيق خفيف وسريع.
تصور مبسط لتدفق العمل المعماري
| المرحلة | ما الذي يحدث؟ | الفائدة |
|---|---|---|
| استقبال البيانات | تصل رسالة كبيرة إلى Service A |
بداية تنفيذ المهمة |
| تقسيم الرسالة | تحويل الرسالة إلى أجزاء صغيرة | تجاوز قيود الحجم |
| تخزين العداد | حفظ عدد الأجزاء في Redis |
تتبع التقدم |
| النشر المتوازي | إرسال كل جزء عبر Pub/Sub |
توزيع الحمل |
| المعالجة | تنفذ Service B كل جزء على حدة |
رفع الكفاءة والمرونة |
| إنقاص العداد | كل عملية منتهية تُخفض القيمة بمقدار واحد | معرفة المتبقي |
| الإكمال النهائي | عند وصول العداد إلى 0 |
تأكيد اكتمال المهمة الأصلية |
نقاط مهمة قبل اعتماد هذا النمط في الإنتاج
1) التقسيم ليس حلاً سحرياً لكل الحالات
هذا الأسلوب فعال عندما تكون البيانات قابلة للتجزئة منطقياً. أما إذا كانت الأجزاء تعتمد على بعضها بشكل كبير، فقد تحتاج إلى نمط مختلف مثل Streaming أو المعالجة المرحلية.
2) يجب الانتباه إلى التزامن
بما أن العمليات تعمل بالتوازي، فمن الضروري أن تكون آلية تحديث العداد آمنة ومتسقة، حتى لا يحدث تضارب في حالة التنفيذ.
3) إدارة الإخفاقات وإعادة المحاولة
في الأنظمة الحقيقية، قد تفشل بعض الأجزاء وتنجح أخرى. لذلك من المهم تصميم منطق واضح لإعادة المحاولة، ومنع احتساب العملية كمكتملة قبل انتهاء جميع الأجزاء فعلاً.
4) مراقبة الأداء
بعد تطبيق هذا النهج، ينبغي تتبع مؤشرات مثل عدد الرسائل، زمن المعالجة، ومعدلات الفشل، للتأكد من أن التقسيم أدى فعلاً إلى تحسين الأداء وليس فقط إلى نقل التعقيد من مكان إلى آخر.
متى يكون هذا الأسلوب خياراً ممتازاً؟
يُعد هذا النهج مناسباً جداً إذا كان نظامك يعاني من أحد السيناريوهات التالية:
- رسائل ضخمة تتجاوز الحد الأقصى المسموح.
- مهام قابلة للتقسيم إلى وحدات مستقلة.
- الحاجة إلى توسيع النظام أفقياً.
- الرغبة في تتبع اكتمال مجموعة عمليات مترابطة بطريقة بسيطة.
الخلاصة التقنية
تقسيم الرسائل الكبيرة إلى مهام صغيرة متوازية هو نمط معماري ذكي لتحسين قابلية التوسع وتجاوز قيود البنية التحتية. وعند دمجه مع Redis كعداد مركزي لتتبع الإنجاز، يصبح من السهل معرفة متى تكتمل المهمة الأصلية بدقة. هذا الحل ليس مناسباً لكل السيناريوهات، لكنه عملي جداً عندما تكون البيانات قابلة للتجزئة وتحتاج إلى معالجة موزعة بكفاءة عالية.