خوارزميات موازنة الحمل
خوارزميات موازنة الحمل
لديك مجموعة من الخوادم وتدفق متواصل من الطلبات الواردة. أي خادم يعالج أي طلب؟ يبدو السؤال بسيطاً حتى تدرك أن الاختيار الخاطئ قد يُوجّه 80% من حركة المرور إلى جهاز واحد بينما تكون الأخرى خاملة، أو يُسبّب ارتفاعاً حاداً في زمن الاستجابة للمستخدمين ذوي الجلسات الثقيلة حين يصلون إلى عقدة مختلفة، أو يُفضي إلى تبريد ذاكرة تخزين مؤقت كانت دافئة. خوارزميات موازنة الحمل هي القواعد التي تحكم هذا التوزيع، واختيار الخوارزمية المناسبة لحمل عملك قرار معماري ذو قيمة حقيقية.
يتناول هذا الدرس الخوارزميات الثلاث التي ستصادفها في تقريباً كل نظام إنتاجي: الدوري (Round Robin)، وأقل الاتصالات (Least Connections)، والتجزئة بالـ IP / التجزئة المتسقة (Consistent Hashing). نفحص كيف تعمل كل واحدة آلياً، وما الظروف والأرقام التي تُبرز أداءها، وأين تفشل، وكيف تختار بينها.
الدوري (Round Robin)
الدوري هو أبسط خوارزمية ممكنة: يُوجَّه الطلب الأول إلى الخادم A، والثاني إلى B، والثالث إلى C، ثم تبدأ الدورة من جديد. يحصل كل خادم على حصة متساوية من الطلبات بالعدد.
يُعيّن نوع الدوري الموزون (Weighted Round Robin) وزناً رقمياً لكل خادم. الجهاز ذو الوزن 3 يستقبل ثلاثة طلبات مقابل كل طلب واحد يذهب إلى جهاز وزنه 1. هذا هو الأسلوب المعياري لدمج خوادم بأحجام مختلفة في نفس المجموعة (مثلاً، عقدة بـ32 معالجاً منطقياً إلى جانب عقدتين بـ8 معالجات).
العيب الجوهري هو أن الدوري أعمى تجاه حمل الخادم. تخيّل أن الخادم B يعالج عملية تصدير دفعي تستغرق 30 ثانية بينما A وC خاملان. يواصل الدوري إرسال الطلبات إلى B بالمعدل نفسه. الخوارزمية تحصي الطلبات لا العمل.
أقل الاتصالات (Least Connections)
تُوجّه خوارزمية أقل الاتصالات (تُعرف أيضاً بـ Least Outstanding Requests) كل طلب جديد إلى الخادم الذي لديه أقل عدد من الاتصالات النشطة حالياً. يحتفظ موازن الحمل بعداد حي لكل خادم، يُزيده عند فتح اتصال ويُنقصه عند إغلاقه.
تخيّل أربعة خوادم بأعداد اتصالات نشطة: 12، 45، 8، 31. الطلب التالي يذهب إلى الخادم الثالث (العدد: 8). بعد رد ذلك الخادم يعود العداد إلى 8، أو يصبح 9 إن وصل طلب آخر قبل اكتمال الأول.
يجمع Weighted Least Connections بين الفكرتين: خادم بوزن 4 و20 اتصالاً نشطاً يُعدّ مكافئاً لخادم بوزن 1 و5 اتصالات (كلاهما بحمل فعّال 5 لكل وحدة سعة). هذا هو الإعداد المعياري لمجموعات الخوادم ذات السعات المختلطة في HAProxy وAWS ALB الإنتاجية.
المقايضة: تتطلب هذه الخوارزمية من موازن الحمل الاحتفاظ بحالة (عدادات الاتصالات). في مجموعة من موازنات حمل متعددة، يجب مزامنة تلك العدادات وإلا عمل كل موازن بتقدير محلي. عند معدلات اتصال مرتفعة جداً (>500 ألف/ثانية)، قد تصبح مسار تحديث العداد نفسه نقطة اختناق. بالنسبة للغالبية العظمى من الأنظمة هذا ليس مصدر قلق عملي.
التجزئة بالـ IP والتجزئة المتسقة
كلٌّ من الدوري وأقل الاتصالات لا حالة لهما من منظور التوجيه — الطلبات المتتالية من نفس العميل قد تصل إلى خوادم مختلفة. بالنسبة للتطبيقات التي تُخزّن حالة الجلسة في الذاكرة (جلسات PHP التقليدية، اتصالات WebSocket، الذواكر المؤقتة داخل العملية)، هذا مشكلة: سلة المشتريات، حالة اللعبة، أو رمز المصادقة غير موجود هناك.
التجزئة بالـ IP تحل هذا بدالة حتمية: server = hash(client_ip) % N حيث N هو عدد الخوادم. نفس الـ IP يُعيّن دائماً إلى نفس الخادم، مما يمنحك جلسات ثابتة دون أي تكلفة لـ cookie أو رمز.
الضعف القاتل يظهر حين يتغير N. إن أضفت أو أزلت خادماً، تتغير كل قيم التجزئة modulo N، ويُعاد تعيين كل عميل موجود تقريباً إلى خادم مختلف. تضيع جميع الحالات الموجودة في الذاكرة في آن واحد — "قطيع الرعد" من إخفاقات الذاكرة المؤقتة.
التجزئة المتسقة تحل مشكلة إعادة التوازن هذه بأناقة. تخيّل فضاء تجزئة من 0 إلى 2³² − 1 مُرتَّباً كحلقة. يُوضع كل خادم عند نقطة واحدة أو أكثر على الحلقة (العقد الافتراضية). يُجزَّأ الطلب الوارد إلى نقطة على الحلقة ثم يُعيَّن إلى أول خادم يُصادفه في اتجاه عقارب الساعة. حين يُضاف أو يُزال خادم، تحتاج فقط المفاتيح التي تقع بينه وبين سابقه على الحلقة إلى الانتقال — حوالي 1/N من جميع المفاتيح لا كلها.
العقد الافتراضية هي مفتاح جعل التجزئة المتسقة تعمل بتوزيع متساوٍ في الممارسة العملية. بدلاً من وضع الخادم A عند نقطة واحدة على الحلقة، يُوضع عند 100 إلى 150 نقطة (كل منها تجزئة مختلفة لـ "Server-A-1"، "Server-A-2"، …). هذا يُنعّم توزيع المفاتيح حتى لا ينتهي بأي خادم بامتلاك قوس غير متناسب من الحلقة. تستخدم كلٌّ من Cassandra وAmazon DynamoDB هذا النهج بالعقد الافتراضية — تُخصّص DynamoDB عادةً 64 إلى 128 عقدة افتراضية لكل مضيف فيزيائي.
أين تتألق التجزئة المتسقة: الذواكر المؤقتة الموزعة (Memcached وRedis Cluster)، موجّهات تجزئة قواعد البيانات، اختيار عقدة الحافة في CDN، وأي خدمة ذات حالة يجب أن يبقى فيها العميل "ملتصقاً" بخادم محدد مع تغيّر عضوية المجموعة بشكل متكرر.
مقارنة جنباً إلى جنب
اختيار الخوارزمية المناسبة
في الممارسة العملية، تجمع معظم الأنظمة خوارزميات مختلفة على طبقات مختلفة بدلاً من اختيار واحدة فقط:
- الخدمات المصغرة عديمة الحالة (API، المصادقة، البحث): الدوري مع فحوصات الصحة. بسيط، منخفض التكلفة، قابل للتوسع أفقياً. استخدم النوع الموزون حين تختلف أحجام الخوادم.
- تكلفة طلب مختلطة أو غير متوقعة (فيديو، ملفات، endpoint ثقيل على قاعدة البيانات): أقل الاتصالات (أو Least Outstanding Requests في موازنات الحمل المدركة لـ HTTP/2 كـ AWS ALB). يُعوّض تلقائياً عن الطلبات البطيئة.
- الذواكر المؤقتة الموزعة، قواعد البيانات المجزأة، مخازن الجلسات: التجزئة المتسقة. تكلفة إعادة تعيين المفاتيح عند تغيير المجموعة أعلى بكثير من تعقيد الخوارزمية.
- اتصالات WebSocket / طويلة الأمد: تجزئة الـ IP أو جلسة ثابتة بالـ Cookie فوق الدوري. الهدف استمرارية الاتصال لا تساوي الحمل.
الخوارزمية متغيّر واحد في مجموعة أكبر من قرارات موازنة الحمل: فترات فحص الصحة، استمرارية الجلسة، تصريف الاتصالات أثناء عمليات النشر، وقواطع الدائرة — كل هذه تتفاعل مع خوارزمية التوجيه. خوارزمية رائعة مقرونة بفحص صحة مفقود ستُرسل الحركة إلى خادم ميت. احتفظ بالصورة الكاملة في ذهنك بينما ننتقل إلى الدرس 5: فحوصات الصحة والتعافي من الأعطال.