نشر خدمات C++ بدون توقف: عندما تكون كل ثانية توقف خسارة مالية
\nتخيل أن خدمة C++ الخلفية لديك تعالج آلاف الطلبات في الثانية. لقد أعددت إصدارًا جديدًا يحتوي على إصلاح حاسم أو تحسين أداء. توقف العملية القديمة، وتشغل الجديدة — ويرى المستخدمون رسالة "503 Service Unavailable". بالنسبة لمتجر إلكتروني، هذه خسارة مبيعات؛ بالنسبة لواجهة API، فقدان للثقة. نشر خدمات C++ بدون توقف يحل هذه المشكلة، مما يسمح لك بتحديث الكود بحيث لا يلاحظ العميل أي انقطاع.
\nفي هذه المقالة، سنستعرض استراتيجيات مجربة: نشر blue-green لتطبيقات C++، نشر rolling update لتطبيقات C++، وتقنية الاستبدال الساخن للملفات التنفيذية. ستحصل على أجزاء جاهزة من كود C++ للإنهاء الآمن (graceful shutdown)، وإدارة الإشارات، وفحص الصحة، والتي يمكنك تطبيقها فورًا في مشروعك.
\n\nلماذا التوقف مشكلة كبيرة، وكيف يغير النشر بدون توقف قواعد اللعبة
\nالنشر التقليدي يبدو كالتالي: kill -9 للعملية القديمة → نسخ الملف التنفيذي الجديد → التشغيل. النتيجة هي توقف يتراوح من 1 إلى 30 ثانية. في عالم الأنظمة عالية التحميل، هذه كارثة. نشر خدمات C++ بدون توقف يضمن أنه في أي لحظة، يوجد إصدار واحد على الأقل من الخدمة يستقبل حركة المرور. المستخدم لا يرى "تحميل"، و DevOps لا يستيقظ ليلًا بسبب التنبيهات.
الفكرة الأساسية بسيطة: نحن لا نقتل العملية القديمة حتى تصبح العملية الجديدة جاهزة تمامًا. بالنسبة لـ C++، هذا مهم بشكل خاص بسبب وقت البدء الطويل (تهيئة مجموعات الخيوط، تحميل القواميس، الاتصال بقاعدة البيانات) والموارد "الثقيلة" التي لا يمكن نقلها ببساطة.
\n\nالاستراتيجية الأولى: نشر Blue-Green لتطبيقات C++
\nالهندسة الكلاسيكية: لديك بيئتان معزولتان تمامًا — Blue (الإصدار الحالي) و Green (الإصدار الجديد). موازن التحميل (nginx, HAProxy) يوجه حركة المرور إلى Blue. تقوم بنشر Green، وتجري اختبارات سريعة (smoke tests)، ثم تحول موازن التحميل إلى Green. يبقى Blue كاحتياطي للتراجع السريع.
\nبالنسبة لـ C++، هذا مثالي إذا كانت الخدمة تخزن الحالة فقط في أنظمة خارجية (قاعدة بيانات، Redis). مثال على تكوين nginx لـ نشر blue-green لتطبيقات C++:
\nupstream backend {\n server 127.0.0.1:8081; # Blue\n # server 127.0.0.1:8082; # Green (معلق مؤقتًا)\n}\n\nserver {\n listen 80;\n location / {\n proxy_pass http://backend;\n proxy_http_version 1.1;\n proxy_set_header Connection "";\n }\n}\nبعد نشر Green على المنفذ 8082، تقوم بإلغاء تعليق السطر الثاني، وتعلق السطر الأول، وتعيد تحميل nginx: nginx -s reload. تستغرق العملية أجزاء من الثانية — لا تنقطع الاتصالات.
كود C++: التحضير للإنهاء الآمن (graceful shutdown)
\nحتى مع نشر blue-green، يجب إنهاء العملية القديمة بشكل صحيح لتجنب فقدان الطلبات قيد المعالجة. أضف معالج الإشارات:
\n#include <csignal>\n#include <atomic>\n#include <iostream>\n\nstd::atomic<bool> stop_requested{false};\n\nvoid signal_handler(int sig) {\n if (sig == SIGTERM || sig == SIGINT) {\n std::cout << "تم استلام طلب الإنهاء" << std::endl;\n stop_requested.store(true);\n }\n}\n\nint main() {\n std::signal(SIGTERM, signal_handler);\n std::signal(SIGINT, signal_handler);\n\n while (!stop_requested.load()) {\n // حلقة الأحداث الخاصة بك، مثلاً epoll_wait مع مهلة\n // process_requests();\n std::this_thread::sleep_for(std::chrono::milliseconds(100));\n }\n\n // هنا أغلق الاتصالات، احفظ الذاكرة المؤقتة، سجل\n std::cout << "تم الانتهاء من الإنهاء الآمن" << std::endl;\n return 0;\n}\nهام: لا تستخدم exit() داخل المعالج — امنح العملية وقتًا لكتابة البيانات. يجب أن يتوقف موازن التحميل عن إرسال الطلبات إلى الخادم القديم، ولكن الطلبات المستلمة بالفعل ستتم معالجتها.