عندما يواجه Rust التحميل الزائد: لماذا تتعطل خدمتك؟
\n\nتخيل أنك كتبت خدمة عالية التحميل مثالية بلغة Rust. إنها سريعة، تستخدم جميع مزايا التجريدات ذات التكلفة الصفرية وإدارة الذاكرة. ولكن هناك مشكلة - يأتي عميل "شره" ويبدأ في إغراق خادمك بآلاف الطلبات في الثانية. ماذا يحدث؟ هل تتعطل قاعدة البيانات؟ هل يختنق قائمة الانتظار؟ أو الأسوأ من ذلك، هل يتم حظرك من قبل API خارجي تتعامل معه؟
\n\nفي Rust، كما في أي لغة أخرى، لا يمكنك الاستغناء عن تحديد معدل الطلبات والتحكم في التدفق. هذه ليست مجرد "ممارسة جيدة" - إنها مسألة بقاء تطبيقك في بيئة الإنتاج. لكن في Rust هناك ميزة واحدة: نحن نحب أن نفعل كل شيء بكفاءة ومن الصفر. لكن لن تضطر إلى إعادة اختراع العجلة - لدينا مكتبات قوية وأنماط أنيقة.
\n\nفي هذه المقالة، سنشرح كيفية تنفيذ تقييد الطلبات في Rust بشكل صحيح، باستخدام كل من الخوارزميات الكلاسيكية (Token Bucket) والأساليب غير المتزامنة الحديثة. ستتعلم كيفية حماية API الخاص بك من هجمات DDoS، وكيفية عدم تجاوز حدود الخدمات الخارجية، وكيفية كتابة أداة التحكم في التدفق الخاصة بك التي ستعمل بشكل أسرع من Go أو Python.
\n\nتحديد معدل الطلبات مقابل التحكم في التدفق: ما الفرق؟
\n\nقبل الغوص في الكود، دعنا نوضح المصطلحات مرة واحدة وإلى الأبد. غالبًا ما يتم الخلط بينهما، لكن الفرق جوهري.
\n\n- \n
- تحديد معدل الطلبات - هي سياسة أمنية. أنت تقول: "يمكن للعميل X إجراء 100 طلب كحد أقصى في الدقيقة". إذا تم تجاوز الحد، تقوم بإرجاع 429 Too Many Requests. هذه حماية للواجهة الخلفية الخاصة بك. \n
- التحكم في التدفق - هي آلية للتحكم في السرعة. أنت تقرر بنفسك السرعة التي تعالج بها الطلبات، حتى لا تفرط في تحميل وحدة المعالجة المركزية أو الإدخال/الإخراج أو قاعدة البيانات. يمكن للعميل إرسال 1000 طلب، لكنك ستعالجها بمعدل 10 في الثانية، والباقي سيتم وضعه في قائمة الانتظار. \n
في Rust، يمكن تنفيذ كلا النهجين باستخدام نفس الأدوات، ولكن بمنطق مختلف. سننظر في كليهما.
\n\nالخوارزميات: Token Bucket و Leaky Bucket
\n\nأشهر الخوارزميات لتحديد معدل الطلبات هي Token Bucket و Leaky Bucket. إنها بسيطة ولكنها فعالة بشكل لا يصدق.
\n\nToken Bucket (دلو الرموز)
\nتخيل دلوًا يتم فيه إسقاط N من الرموز كل ثانية. عندما يأتي طلب، يأخذ رمزًا واحدًا. إذا كان الدلو فارغًا، يتم رفض الطلب. Rust مثالية لتنفيذ هذه الخوارزمية بفضل العمليات الذرية.
\n\nLeaky Bucket (الدلو المتسرب)
\nهنا، يحتوي الدلو على ثقب في القاع يتدفق من خلاله عدد ثابت من الطلبات في الثانية. تدخل الطلبات إلى الدلو من الأعلى. إذا فاض الدلو، يتم تجاهل الطلبات الزائدة. هذه الخوارزمية أفضل لتنعيم الأحمال الذروة.
\n\nفي Rust، لن نكتب هذه الخوارزميات يدويًا (على الرغم من أن ذلك مفيد تعليميًا). بدلاً من ذلك، سنستخدم مكتبة governor - المعيار الذهبي لتحديد معدل الطلبات في نظام Rust البيئي.
التطبيق العملي: تثبيت Governor
\n\nمكتبة governor هي تطبيق سريع وغير متزامن وآمن للخيوط لخوارزمية Token Bucket. تدعم كلاً من الأوضاع المتزامنة وغير المتزامنة، وتعمل مع tokio و async-std.
نضيف إلى Cargo.toml:
[dependencies]\ngovernor = "0.6"\ntokio = { version = "1", features = ["full"] }\n\n\nوالآن لنكتب أداة بسيطة لتحديد معدل الطلبات لخادم HTTP يعتمد على hyper أو axum. للتبسيط، سنستخدم axum.
use axum::{\n extract::Request,\n http::StatusCode,\n middleware::{self, Next},\n response::Response,\n routing::get,\n Router,\n};\nuse governor::{DefaultDirectRateLimiter, Quota, RateLimiter};\n