نشر Canary باستخدام Rust: بلا خوف ولا تردد
\n\nتخيل: لقد كتبت ميزة جديدة بلغة Rust، وأجريت الاختبارات (جميعها ناجحة!)، ونشرتها على الإنتاج... وفجأة تظهر أخطاء 500، ويبدأ المستخدمون في الاتصال بالدعم، وأنت في حالة من الفوضى لاسترجاع الإصدار. هل هذا مألوف؟ إذا كان الأمر كذلك، فأنت لست وحدك. النشر دائمًا ما يكون مرهقًا، خاصة عندما يعمل الكود مع الأجهزة أو تحت الأحمال العالية.
ولكن هناك طريقة لجعل النشر شبه خالٍ من الألم: نشر Canary (النشر التجريبي). الفكرة بسيطة: تقوم بتوجيه الكود الجديد ليس لجميع المستخدمين دفعة واحدة، بل لنسبة صغيرة (على سبيل المثال، 5-10%). إذا كان كل شيء على ما يرام، تزيد النسبة تدريجيًا. إذا لم يكن الأمر كذلك، تسترجع الإصدار، ولا يعلم بذلك سوى حفنة من "الكناري".
\n\nفي هذه المقالة، سنشرح كيفية تنفيذ نشر Canary لخدمة مبنية بلغة Rust. ستشاهد أمثلة الكود، وإعداد موازن التحميل، ومراقبة بسيطة. هيا بنا!
\n\nلماذا Rust مثالي لنشر Canary؟
\n\nRust ليست مجرد لغة أخرى. ميزاتها تجعل النشر التجريبي فعالاً بشكل خاص:
\n\n- \n
- أمان الذاكرة — تضمن Rust عدم تسرب الذاكرة أو سباقات البيانات (data races) إلى الإنتاج. أخطاء أقل = فرصة أقل لفشل الكناري. \n
- حجم ثنائي صغير —
cargo build --releaseينتج ملفًا تنفيذيًا مضغوطًا. يمكن نسخه بسرعة إلى عدة خوادم أو حاويات. \n - أداء عالٍ — تعمل Rust بمستوى C/C++. هذا يعني أن خادم الكناري الخاص بك لن يتراجع تحت الحمل، حتى لو تم توجيه 10% من حركة المرور إليه. \n
- النظام البيئي — مكتبات مثل
actix-webوtokioوtowerتوفر أدوات جاهزة لـ HTTP والموازنة والمقاييس. \n
النتيجة: خدمة Rust مستقرة وسريعة وسهلة الإدارة الدقيقة لحركة المرور. مثالية لنشر Canary!
\n\nهندسة نشر Canary باستخدام Rust
\n\nلنفترض أن لدينا خدمة ويب مبنية على actix-web. API REST عادي: يستقبل الطلبات، ويكتب في قاعدة البيانات، ويعيد الردود. نريد تحديث الإصدار من v1 إلى v2، ولكن ليس دفعة واحدة.
المخطط الأساسي:
\n\n- \n
- موازن التحميل (مثل Nginx أو HAProxy) يوزع حركة المرور بين الإصدارات. \n
- المجموعة الرئيسية (المستقرة) — خوادم v1، تستقبل 90% من الطلبات. \n
- مجموعة Canary — خادم أو خادمان مع v2، يستقبلان 10% من الطلبات. \n
- المراقبة — نجمع المقاييس من كلا المجموعتين: الأخطاء، زمن الاستجابة، عدد الطلبات في الثانية. \n
عندما نتأكد من أن v2 يعمل بشكل مستقر، نزيد وزن مجموعة Canary تدريجيًا حتى 100%.
\n\nالخطوة 1: كتابة خادم Rust مع المقاييس
\n\nسيقوم خادمنا بتقديم إصدارين من نقطة النهاية /api/hello. للتبسيط، يختلف الإصداران في سلسلة الرد. في مشروع حقيقي، قد يكون هذا منطق عمل مختلفًا.
// Cargo.toml\n[dependencies]\nactix-web = "4"\nactix-rt = "2"\nserde = { version = "1", features = ["derive"] }\nprometheus = { version = "0.13", features = ["process"] }\nlog = "0.4"\nenv_logger = "0.10"\n\nالآن الخادم نفسه. سنضيف مقاييس Prometheus لمراقبة صحة الكناري:
\n\nuse actix_web::{web, App, HttpServer, HttpResponse, middleware::Logger};\nuse prometheus::{Counter, HistogramVec, register_counter, register_histogram_vec};\nuse std::sync::OnceLock;\n\n// المقاييس العامة\nstatic REQUEST_COUNT: OnceLock<Counter> = OnceLock::new();\nstatic REQUEST_DURATION: OnceLock<HistogramVec> = OnceLock::new();\n\nfn init_metrics() {\n REQUEST_COUNT.get_or_init(|| {\n register_counter!(\n "http_requests_total",\n "Total number of HTTP requests"\n ).unwrap()\n });\n REQUEST_DURATION.get_or_init(|| {\n register_histogram_vec!(\n "http_reques