تقييد المعدل والتحكم في التدفق في Go: دليل كامل مع أمثلة

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

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

ابدأ الدورة

تقييد المعدل والتحكم في التدفق في Go: كيف تحمي API وتحافظ على هدوئك

\n\n

تخيل: لقد كتبت API مثاليًا بلغة Go. إنه سريع للغاية، يعالج الطلبات في أجزاء من الثانية، وفجأة! ينهال على الخادم سيل من الطلبات. قد يكون بوتًا، أو خطأ في العميل، أو مجرد ضجة حول منتجك. بدون حماية، سيتعطل الخادم، وسيغادر المستخدمون، وستقضي الليل في تحليل السجلات.

\n\n

هل هذا مألوف؟ إذًا تقييد المعدل والتحكم في التدفق هما أفضل صديقين جديدين لك. في هذه المقالة على pythonlib.ru، سنشرح كيفية تنفيذهما في Go - اللغة المصممة للأنظمة عالية التحميل. لا ماء، فقط كود حي وحالات استخدام حقيقية.

\n\n

تقييد المعدل مقابل التحكم في التدفق: ما الفرق؟

\n\n

بداية، دعنا نرتب المصطلحات. كثيرون يخلطون بين هذه المفاهيم، لكن هناك فرق.

\n\n
    \n
  • تقييد المعدل - هو تحديد صارم: "لا يزيد عن 100 طلب في الدقيقة". إذا تم تجاوز الحد، يستجيب الخادم بـ 429 Too Many Requests. الأمر عادل وحازم.
  • \n
  • التحكم في التدفق - هو تنظيم مرن: "سنبطئك، لكننا لن نحظرك". على سبيل المثال، بدلاً من الرد على جميع الطلبات فورًا، نضعها في قائمة انتظار ونعالجها بسرعة ثابتة.
  • \n
\n\n

في Go، يتم تنفيذ كلا النهجين بأناقة بفضل goroutines والقنوات. دعنا نلقي نظرة على أمثلة محددة.

\n\n

أبسط محدد معدل باستخدام القنوات

\n\n

الطريقة الأساسية لتحديد السرعة هي استخدام الموقت من حزمة time. إنه مثل بندول الإيقاع: كل "نقرة" تسمح بطلب واحد.

\n\n
package main\n\nimport (\n    "fmt"\n    "time"\n)\n\nfunc main() {\n    // إنشاء قناة ترسل الرموز كل 200 مللي ثانية\n    limiter := time.Tick(200 * time.Millisecond)\n\n    // محاكاة 10 طلبات\n    for i := 1; i <= 10; i++ {\n        <-limiter // انتظار الإذن\n        fmt.Printf("تمت معالجة الطلب %d في %s\\n", i, time.Now().Format("15:04:05.000"))\n    }\n}
\n\n

ماذا يحدث هنا؟ time.Tick ينشئ قناة ترسل قيمة كل 200 مللي ثانية. يتم حظر goroutine الخاص بنا عند <-limiter حتى يحصل على رمز. النتيجة: 5 طلبات بالضبط في الثانية، دون خيارات.

\n\n

العيب: إذا كان عدد الطلبات أقل من عدد النقرات، فإننا ننتظر فقط. غير فعال؟ نعم. لكنه مثالي للسيناريوهات البسيطة.

\n\n

تحديد المعدل مع الاندفاع باستخدام المخزن المؤقت

\n\n

ماذا لو احتجنا للسماح بـ "انفجارات" حركة المرور؟ على سبيل المثال، السماح بـ 10 طلبات فورية، ثم التحديد إلى 5 في الثانية. لهذا نستخدم قناة مخزنة - Token Bucket الكلاسيكي.

\n\n
package main\n\nimport (\n    "fmt"\n    "time"\n)\n\nfunc main() {\n    // قناة مخزنة تتسع لـ 5 رموز\n    tokens := make(chan struct{}, 5)\n\n    // Goroutine المملئ: يضيف رمزًا كل 200 مللي ثانية\n    go func() {\n        ticker := time.NewTicker(200 * time.Millisecond)\n        defer ticker.Stop()\n        for range ticker.C {\n            select {\n            case tokens <- struct{}{}:\n            default:\n                // القناة ممتلئة - لا تفعل شيئًا\n            }\n        }\n    }()\n\n    // محاكاة 15 طلبًا\n    for i := 1; i <= 15; i++ {\n        <-tokens // أخذ رمز (انتظار إذا كان فارغًا)\n        fmt.Printf("تمت معالجة الطلب %d في %s\\n", i, time.Now().Format("15:04:05.000"))\n        time.Sleep(50 * time.Millisecond) // تأخير بسيط بين الطلبات\n    }\n}
\n\n

الطلبات الخمسة الأولى ستمر فورًا (المخزن المؤقت ممتلئ)، ثم بعد ذلك طلب واحد كل 200 مللي ثانية. هذا النمط مثالي لـ API حيث قد يبدأ العميل فجأة في إرسال العديد من الطلبات (على سبيل المثال، بعد إعادة التشغيل).

\n\n

التحكم في التدفق عبر WaitGroup ومجموعة العمال

\n\n

الآن ننتقل إلى التحكم في التدفق. لنفترض أن لدينا قائمة انتظار من المهام (على سبيل المثال، إرسال بريد إلكتروني جماعي). لا نريد حظر العميل، ولكننا أيضًا لا نريد إرهاق الخادم.

المدونات

توصيات الكتب