لماذا النشر بدون توقف ليس رفاهية بل ضرورة لمطور Swift
\nتخيل: خدمة Swift الخاصة بك على Vapor تعالج آلاف الطلبات في الثانية. تقوم بإصدار ميزة جديدة - ويحصل المستخدمون على 502 Bad Gateway، وفي السجلات - SIGTERM واتصالات مقطوعة. مألوف؟ إذا كنت تكتب بلغة Swift للخادم الخلفي، فستواجه عاجلاً أم آجلاً مشكلة: كيف تحديث الخدمة دون فقدان أي طلب.
النشر بدون توقف ليس سحراً، بل مجموعة من التقنيات المثبتة التي تسمح باستبدال الملف الثنائي العامل لـ Swift بملف جديد دون قطع الاتصالات النشطة. في هذه المقالة، سأوضح كيفية إعداد الإيقاف السلس، وتنظيم التبديل الساخن للعمليات، وجعل خدمة Swift تعمل مع Nginx. كل ذلك مع كود حقيقي بلغة Swift وبدون حشو.
\n\nما هو النشر بدون توقف ولماذا هو مهم لمشاريع Swift
\nالنشر بدون توقف (Zero-downtime deployment) هو عندما تقوم بتحديث التطبيق ولا يلاحظ المستخدمون أي جزء من الألف من الثانية من التوقف. بالنسبة لخدمات Swift، هذا مهم بشكل خاص لأن:
\n- \n
- العملاء لا يتحملون الأخطاء - الطلب المقطوع = بيانات أو أموال مفقودة. \n
- خوادم Swift غالباً ما تعمل تحت حمولة عالية (التكنولوجيا المالية، الدردشات، API الوقت الفعلي). \n
- إعادة تشغيل الملف الثنائي قد تستغرق ثوانٍ - وهذا وقت طويل بالنسبة لاتصال نشط. \n
النهج الكلاسيكي - قتل العملية القديمة وتشغيل عملية جديدة - لا يعمل. يجب على تطبيق Swift على Vapor أو Hummingbird إنهاء معالجة الطلبات الحالية بسلاسة، ثم إفساح المجال لمثيل جديد. هذا هو موضوع كل الكود التالي.
\n\nالخطوة 1. الإيقاف السلس في Swift: كتابة إنهاء صحيح
\nاللبنة الأولى للنشر بدون توقف هي الإيقاف السلس. يجب أن تكون خدمة Swift الخاصة بك قادرة على التقاط إشارة SIGTERM وعدم قطع الاتصالات في منتصف الطريق. Vapor 4+ يفعل ذلك بالفعل بشكل افتراضي، لكن دعنا نتحقق، وإذا لزم الأمر، نضيف منطقاً مخصصاً.
الإعداد الأساسي لـ Vapor للإيقاف السلس
\nimport Vapor\n\nlet app = Application()\n\n// تسجيل معالج الإشارة\napp.lifecycle.use(ShutdownSignalHandler())\n\n// كود Vapor القياسي\napp.get("health") { req in\n return "OK"\n}\n\ntry app.run()\n\nلكن ماذا لو كنت بحاجة إلى انتظار اكتمال المهام الطويلة؟ على سبيل المثال، معالجة الملفات أو إرسال البريد الإلكتروني. دعنا نضيف ShutdownSignalHandler مخصصاً:
import Vapor\nimport NIOCore\n\nfinal class ShutdownSignalHandler: LifecycleHandler {\n private var runningTasks: [EventLoopFuture<Void>] = []\n \n func willBoot(_ app: Application) throws {\n // الاشتراك في SIGTERM\n app.running?.onStop = {\n print("⏳ تم استلام إشارة الإيقاف. إنهاء المهام النشطة...")\n return self.runningTasks.flatten(on: app.eventLoopGroup.next())\n }\n }\n \n func registerTask(_ task: EventLoopFuture<Void>) {\n runningTasks.append(task)\n }\n}\n\nالآن، عندما تصل SIGTERM، سينتظر الخادم اكتمال جميع المهام المسجلة. هذا أمر بالغ الأهمية للنشر بدون توقف لخدمات Swift: العملية القديمة لن تقتل الطلبات التي بدأت بالفعل في معالجتها.
التحقق في وحدة التحكم
\nقم بتشغيل الخادم وأرسل إليه إشارة:
\nkill -SIGTERM <PID>\n\nفي السجلات، سترى رسالة حول الإنهاء السلس. إذا كان كل شيء يعمل - انتقل إلى الخطوة التالية.
\n\nالخطوة 2. النشر الأزرق-الأخضر على Swift: ملفان ثنائيان - بدون توقف
\nالإيقاف السلس يحل مشكلة قطع الاتصالات، لكنه لا يحل مشكلة وقت التحديث. بينما تنتهي العملية القديمة، يجب أن تستقبل العملية الجديدة الطلبات بالفعل. المخطط المثالي هو النشر الأزرق-الأخضر.
\nالجوهر بسيط: لديك مثيلان لخدمة Swift (أزرق وأخضر). واحد نشط، والثاني في وضع الاستعداد. عندما تقوم بتحديث الكود، فإنك:
\n- \n