تحديد المعدل (Rate Limiting) والتحكم في التدفق (Throttling) في Kotlin: كيفية حماية API من الأحمال الزائدة

مدرب بايثون أونلاين للمبتدئين

تعلم Python بسهولة دون إرهاق نظري. حل مهام عملية مع التحقق التلقائي واكتب الكود مباشرة في المتصفح.

ابدأ الدورة

عندما يصرخ API الخاص بك "SOS": التعرف على تحديد المعدل (Rate Limiting) والتحكم في التدفق (Throttling)

\n\n

تخيل: لقد كتبت تطبيق Kotlin مثاليًا. إنه سريع، أنيق، ويستخدم الكوروتينات و Flow. قمت بنشره في الإنتاج، وفجأة! مستخدم "لطيف" واحد (أو الأسوأ من ذلك، مهاجم) يبدأ في قصف نقطة النهاية الخاصة بك بآلاف الطلبات في الثانية. الخادم ينهار، قاعدة البيانات تبكي، وبقية المستخدمين يتلقون 503 Service Unavailable.

\n\n

ألم مألوف؟ لهذا السبب تحديدًا توجد آليات تحديد المعدل (rate limiting) و التحكم في التدفق (throttling). في هذه المقالة، لن نتحدث فقط عن النظرية، بل سنشرح من خلال أمثلة عملية كيفية تطبيق هذه الأنماط في Kotlin.

\n\n

سنكتب كودًا يحمي حقًا الواجهة الخلفية الخاصة بك، سواء كان تطبيقًا على Ktor أو Spring Boot أو كوروتين خالص. هيا بنا!

\n\n

تحديد المعدل (Rate Limiting) مقابل التحكم في التدفق (Throttling): ما الفرق (ولماذا هو مهم)؟

\n\n

لنبدأ بالأساسيات لتجنب الخلط بين المصطلحات. على الرغم من استخدام هذه المفاهيم غالبًا كمرادفات، إلا أن هناك فارقًا دقيقًا بينهما.

\n\n
    \n
  • تحديد المعدل (Rate Limiting) — هو حد صارم. "يمكنك فقط إجراء 100 طلب في الدقيقة. بعد الطلب 101، ستحصل على 429 Too Many Requests وحظر حتى الدقيقة التالية". هذا يتعلق بـ الحد.
  • \n
  • التحكم في التدفق (Throttling) — هو تنظيم أكثر ليونة. "لا يمكنك إرسال الطلبات أسرع من مرة واحدة كل 200 مللي ثانية. إذا أرسلت أسرع، فسنضعها في قائمة انتظار أو نتجاهلها ببساطة". هذا يتعلق بـ السرعة.
  • \n
\n\n

في عالم Kotlin، غالبًا ما ننفذ تحديد المعدل على مستوى API (middleware) والتحكم في التدفق على مستوى العمليات المحددة (على سبيل المثال، لتجنب إغراق خادم خارجي بالطلبات).

\n\n

الخوارزميات التي يجب أن يعرفها كل مطور Kotlin

\n\n

قبل كتابة الكود، دعنا نختار الأداة. هناك ثلاث خوارزميات كلاسيكية تتناسب تمامًا مع بنية Kotlin.

\n\n

1. Token Bucket (دلو الرموز) — الأكثر شيوعًا

\n

تخيل دلوًا، يتم إسقاط N من الرموز الجديدة فيه كل ثانية. للدلو سعة قصوى (burst). لمعالجة طلب، تحتاج إلى أخذ رمز. إذا لم تكن هناك رموز، يتم رفض الطلب.

\n

المزايا: يسمح باندفاعات صغيرة من الحمل (bursts)، سهل التنفيذ.

\n\n

2. Leaky Bucket (الدلو المثقوب) — تنعيم القمم

\n

دلو به ثقب. تدخل الطلبات إلى الدلو، ويخرجها "الثقب" بسرعة ثابتة. إذا امتلأ الدلو، يتم تجاهل الطلبات.

\n

المزايا: يوازن الحمل بشكل مثالي، ولا يسمح بوجود قمم.

\n\n

3. Sliding Window (النافذة المنزلقة) — الدقة بدون هزات

\n

بدلاً من الحدود الصارمة (كما في "دقيقة واحدة")، ننظر إلى فترة زمنية منزلقة. أكثر تعقيدًا في التنفيذ، لكنه يعكس الحمل الحقيقي بدقة أكبر.

\n\n

لمشاريع Kotlin، Token Bucket هو الوسط الذهبي بين البساطة والكفاءة. هذا ما سنقوم بتنفيذه.

\n\n

كتابة الكود: محدد معدل (Rate Limiter) في Kotlin من الصفر (بدون مكتبات)

\n\n

دعنا ننشئ محدد معدل بسيطًا ولكنه عملي، باستخدام الكوروتينات و Mutex. ستكون هذه نسخة آمنة للخيوط من Token Bucket.

\n\n
import kotlinx.coroutines.sync.Mutex\nimport kotlinx.coroutines.sync.withLock\nimport kotlin.time.Duration\nimport kotlin.time.TimeSource\n\nclass TokenBucketRateLimiter(\n    private val maxTokens: Int,\n    private val refillRate: Long, // رمز في الثانية\n    private val timeSource: TimeSource = TimeSource.Monotonic\n) {\n    private var tokens: Long = maxTokens.toLong()\n    private var lastRefillTimestamp: Long = timeSource.markNow().elapsedNow().inWholeMilliseconds\n    private val mutex = Mutex()\n\n    suspend fun tryAcquire(): Boolean {\n        mutex.withLock {\n            refillTokens()\n            return if (tokens > 0) {\n                tokens--\n                true\n            } else {\n                

المدونات

توصيات الكتب