يا جماعة الخير، السلام عليكم ورحمة الله وبركاته. معكم أخوكم أبو عمر.
خلوني أحكي لكم قصة صارت معي ومع فريقي قبل كم سنة، قصة فيها “أكشن” وسهر للصبح وقهوة ما الها آخر. كنا وقتها أطلقنا واجهة برمجية (API) جديدة لمنتجنا، وشغلنا كان مرتب والحمد لله. الأمور كانت ماشية زي الحلاوة، والمستخدمين مبسوطين، وإحنا بنراقب الأداء ومكيفين على حالنا.
لحد ما إجت ليلة ما بنساها. حوالي الساعة 2 بعد نص الليل، بدأت توصلنا تنبيهات زي المطر: “CPU Usage at 99%”, “Database Connections Maxed Out”, “High Latency Detected”. الخوادم كانت بتصرخ وبتستنجد، والموقع صار أبطأ من سلحفاة طالعة مرتفع. والله يا جماعة، شعور ما بوصفه، كأنك شايف ابنك بتعب قدامك ومش عارف شو تعمل.
نزلنا كلنا على الخط، أنا والشباب، وبدأنا رحلة البحث عن السبب. فحصنا الـ logs، حللنا الشبكة، عملنا كل إشي ممكن يخطر على بالكم. وبعد ساعات من التمحيص والتدقيق، وقع الفاس بالراس. لقينا المصيبة: عنوان IP واحد، بس واحد، قاعد بيبعت آلاف الطلبات في الدقيقة على endpoint واحد حساس ومكلف من ناحية معالجة. كان بيستنزف كل مواردنا بدون رحمة. ممكن يكون بوت سيء النية، أو ممكن يكون مجرد مطور مبتدئ عمل سكربت فيه loop لا نهائي. النتيجة واحدة: نظامنا على وشك الانهيار الكامل.
في هذيك الليلة، بعد ما عملنا حظر يدوي للـ IP المشبوه، أخذنا قرار صارم: “ممنوع أي API جديد يطلع بدون تطبيق سياسة تحديد معدل واضحة”. كانت ليلة صعبة، بس تعلمنا منها درس قاسي ومهم، وهو الدرس اللي جاي أشاركم إياه اليوم. “تحديد المعدل” أو الـ Rate Limiting مش رفاهية، يا جماعة، هاد هو حارس البوابة تبع تطبيقك.
ما هو “تحديد المعدل” (Rate Limiting) ببساطة؟
تخيل معي إنو الـ API تبعك عبارة عن مطعم فاخر. الزباين (المستخدمين والتطبيقات الأخرى) بيجوا يطلبوا أطباق (بيانات). المطبخ (الخادم) عنده قدرة استيعابية معينة لتحضير الطلبات. لو إجا فجأة 1000 زبون في نفس اللحظة وطلبوا أطباق معقدة، شو راح يصير؟ راح تصير فوضى عارمة، المطبخ راح ينهار، والزباين اللي بينتظروا راح يتأخروا كثير وممكن ما يوصلهم طلبهم أصلاً.
الـ Rate Limiting هو ببساطة “مدير الصالة” أو “الحارس” اللي بينظم دخول الزباين. هو بيحكي لكل زبون: “أهلاً وسهلاً فيك، بس مسموح لك تطلب 100 طبق في الساعة فقط، عشان نعطي فرصة لغيرك والمطبخ ما ينضغط”.
تقنياً، هو آلية للتحكم في عدد الطلبات التي يمكن للمستخدم (أو عنوان IP، أو مفتاح API) إرسالها إلى الخادم خلال فترة زمنية معينة. إذا تجاوز المستخدم هذا الحد، يتم رفض طلباته مؤقتاً مع إعلامه بذلك.
ليش وجع الراس هاد كله؟ فوائد تحديد المعدل
ممكن واحد يسأل: “يا أبو عمر، ليش أغلب حالي وأعقد النظام؟”. الجواب بسيط: لأن تكلفة عدم فعله أكبر بكثير. الفوائد أهم من وجع الراس المبدئي:
- الحماية من هجمات الحرمان من الخدمة (DDoS): الـ Rate Limiting هو خط الدفاع الأول ضد الهجمات البسيطة والمتوسطة اللي بتحاول تغرق خادمك بالطلبات.
- منع الاستغلال وسوء الاستخدام: بيمنع البوتات من كشط بيانات موقعك (Scraping)، وبيصعّب هجمات تخمين كلمات المرور (Brute-force attacks) على نقاط الدخول (login endpoints).
- ضمان جودة الخدمة والعدالة: بيضمن إنو مستخدم واحد سيء التصرف ما يأثر على تجربة باقي المستخدمين. الكل بياخذ حصته العادلة من الموارد.
- إدارة التكاليف: في عالم الحوسبة السحابية، كل طلب وكل دورة معالج (CPU cycle) إلها تكلفتها. تحديد المعدل بيمنع الفواتير المفاجئة والصادمة آخر الشهر.
أشهر خوارزميات تحديد المعدل
الجميل في الموضوع إنو في طرق مجربة ومدروسة لتطبيق الـ Rate Limiting. ما في داعي تخترع العجلة. خلينا نشوف أشهر الخوارزميات:
خوارزمية دلو التوكن (Token Bucket)
هاي من أشهر وأفضل الخوارزميات. تخيل عندك دلو (bucket) بيتعبى فيه “توكنز” (قطع نقدية) بمعدل ثابت (مثلاً 10 توكنز كل ثانية). كل طلب بيوصل للـ API لازم ياخذ توكن واحد من الدلو عشان يتم تمريره. لو الدلو كان فاضي، الطلب بيترفض أو بينتظر لحد ما يتعبى توكن جديد. هذا بيسمح بحدوث “رشقات” (Bursts) من الطلبات طالما في توكنز كافية في الدلو، وهذا بيعطي مرونة ممتازة.
خوارزمية الدلو المتدفق (Leaky Bucket)
هون الفكرة مختلفة شوي. تخيل دلو فيه ثقب من تحت بيسمح بتسريب المي بمعدل ثابت. الطلبات اللي بتوصل هي المي اللي بتنصب في الدلو. الخادم بيعالج الطلبات بالمعدل اللي بتتسرب فيه من تحت. لو انصب مي كثير بسرعة (طلبات كثيرة)، الدلو راح يتعبى ويفيض، والطلبات الزائدة راح يتم رفضها. هاي الخوارزمية ممتازة لتسوية وتيرة الطلبات (smoothing out traffic) وجعلها منتظمة.
خوارزمية النافذة الثابتة (Fixed Window Counter)
هاي أبسط خوارزمية. الفكرة هي إنك بتحدد نافذة زمنية (مثلاً دقيقة) وعداد. كل طلب بيوصل ضمن هاي الدقيقة بيزيد العداد. لو وصل العداد للحد الأقصى (مثلاً 100 طلب)، كل الطلبات اللي بتيجي بعده لنهاية الدقيقة بيتم رفضها. عند بداية الدقيقة الجديدة، العداد بيرجع للصفر. مشكلتها إنو ممكن مستخدم يبعت 100 طلب في آخر ثانية من الدقيقة الأولى، و100 طلب في أول ثانية من الدقيقة الثانية، وهيك بيكون بعت 200 طلب في ثانيتين، وهذا ممكن يسبب ضغط كبير.
خوارزمية النافذة المنزلقة (Sliding Window)
هاي هي النسخة المحسنة من اللي قبلها. بدل ما تحسب الطلبات في نوافذ منفصلة، هي بتحسبها في نافذة زمنية “منزلقة”. يعني في أي لحظة، هي بتشوف عدد الطلبات في آخر 60 ثانية بالضبط. هذا بحل مشكلة “الحواف” اللي حكينا عنها في النافذة الثابتة ويعتبر حل أدق وأكثر أماناً.
مثال عملي: لنطبق هذا الكلام (Node.js/Express)
حكينا كثير نظري، خلينا نشوف شوية كود. بما إني بحب الشغل يكون عملي، هاي طريقة سهلة جداً لتضيف Rate Limiting لتطبيق Express.js باستخدام مكتبة مشهورة اسمها express-rate-limit.
أولاً، ثبت المكتبة:
npm install express-rate-limit
بعدين، في ملف التطبيق الرئيسي تبعك (مثلاً app.js)، ضيف الكود التالي:
const express = require('express');
const rateLimit = require('express-rate-limit');
const app = express();
// إعداد عام لتحديد المعدل لكل الـ APIs
const limiter = rateLimit({
windowMs: 15 * 60 * 1000, // نافذة زمنية: 15 دقيقة
max: 100, // الحد الأقصى: 100 طلب لكل IP خلال الـ 15 دقيقة
message: 'طلبات كثيرة جداً من هذا الـ IP، يرجى المحاولة مرة أخرى بعد 15 دقيقة',
headers: true, // إرسال معلومات الحد في الـ headers (RateLimit-Limit, RateLimit-Remaining)
});
// تطبيق الإعداد على كل المسارات
app.use(limiter);
// يمكن تطبيق إعدادات خاصة لمسارات حساسة
const loginLimiter = rateLimit({
windowMs: 60 * 60 * 1000, // ساعة واحدة
max: 5, // 5 محاولات تسجيل دخول فاشلة فقط لكل ساعة
message: 'محاولات تسجيل دخول كثيرة جداً، تم حظر هذا الـ IP مؤقتاً لمدة ساعة',
});
app.post('/login', loginLimiter, (req, res) => {
// ... منطق تسجيل الدخول
});
app.get('/api/data', (req, res) => {
res.send('هذه هي بياناتك القيمة!');
});
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => console.log(`الخادم يعمل على المنفذ ${PORT}`));
شوفوا ما أبسطه! في دقائق معدودة، أنت أضفت طبقة حماية أساسية وقوية جداً لتطبيقك. لاحظ كيف عملنا حد عام لكل التطبيق، وحد آخر أكثر صرامة على مسار تسجيل الدخول تحديداً.
نصائح أبو عمر الذهبية 💡
من واقع خبرتي، هاي شوية نصائح من القلب:
- ابدأ متساهلاً ثم تشدد: لما تبدأ، حط حدود منطقية وعالية شوي. راقب سلوك المستخدمين الطبيعي وبعدين ابدأ تضيق الخناق على الأرقام اللي بتشوفها مناسبة. لا تخنق مستخدمينك الشرعيين.
- لكل مقامٍ مقال: مش كل الـ endpoints زي بعض. الـ
/loginبيحتاج حماية صارمة ضد تخمين كلمات المرور. الـ/searchبيحتاج حد متوسط. الـ/public-dataممكن يكون حده أعلى بكثير. خصص لكل مسار ما يناسبه. - أبلغ المستخدم بوضوح: لما ترفض طلب بسبب تجاوز الحد، استخدم كود الحالة الصحيح وهو
429 Too Many Requests. والأفضل من هيك، استخدم الـ headers مثلRetry-Afterعشان تخبر المستخدم متى ممكن يحاول مرة ثانية. هاي من أفضل الممارسات في تصميم الـ API. - المراقبة ثم المراقبة ثم المراقبة: ضيف logging على الطلبات اللي بيتم رفضها. هذا بيعطيك فكرة عن مين اللي بيحاول يتجاوز الحدود، وهل هي هجمات حقيقية أو مجرد استخدام خاطئ. البيانات هي كنزك.
- حدد استراتيجيتك: هل ستحدد المعدل بناءً على الـ IP؟ أم على هوية المستخدم (User ID) بعد تسجيل الدخول؟ أم على مفتاح الـ API؟ لكل منها استخداماته. الـ IP هو العام، لكن المستخدم أو مفتاح الـ API أدق وبيسمح لمستخدم واحد يستخدم جهازين مختلفين بدون ما يحظر حاله.
الخلاصة: الحارس الذي لا ينام
يا جماعة الخير، في عالم اليوم الرقمي، بناء تطبيق بدون Rate Limiting يشبه بناء قلعة بدون أسوار أو حراس. هي مسألة وقت فقط قبل أن يأتي من يستغل هذا الضعف، سواء عن قصد أو عن غير قصد.
تحديد المعدل ليس مجرد أداة تقنية، بل هو فلسفة تصميم تضمن الاستقرار والأمان والعدالة لتطبيقك. هو الحارس الأمين الذي يعمل 24/7 ليحمي مواردك ويضمن أن خدمتك تظل متاحة للجميع. لا تستهينوا بأهميته، وابدأوا بتطبيقه من اليوم الأول في مشاريعكم. 🛡️
يلا، شدوا حيلكم، والله يوفقكم في مشاريعكم. وأي سؤال، أخوكم أبو عمر حاضر.