نشر خدمات TypeScript بدون توقف: دليل خطوة بخطوة

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

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

ابدأ الدورة

كيف تقوم بتحديث الخدمة دون إيقاظ المستخدمين؟



تخيل: يوم الجمعة، في المساء، قررت نشر إصلاح سريع للخلل على الإنتاج. تضغط على deploy - وتتعطل خدمة المراسلة الفورية أو السينما عبر الإنترنت أو CRM لمدة 30 ثانية. آلاف المستخدمين في هذه اللحظة يرون 502 Bad Gateway. وإذا كانت بوابة دفع؟ احسب الخسائر.



النشر بدون توقف (Zero-downtime deployment) ليس رفاهية، بل معيار لخدمات TypeScript الحديثة. في هذه المقالة، سنشرح التقنيات الحقيقية المستخدمة في الإنتاج: من الإنهاء المهذب إلى النشر الأزرق-الأخضر والتحديث المتدرج. ستحصل على أنماط جاهزة للتطبيق بلغة TypeScript.



لماذا النشر العادي لتطبيق TypeScript خطير؟



عند تشغيل npm run build && node dist/index.js، تحدث عدة مشاكل:



  • قطع الاتصالات - يتم قتل العملية القديمة بينما لا تزال الطلبات الجديدة قادمة.
  • فقدان البيانات في الذاكرة - الجلسات غير المحفوظة، ذاكرة التخزين المؤقت، قوائم الانتظار.
  • بداية قذرة - العملية الجديدة لم تسخن بعد، ولكنها تتلقى حركة المرور بالفعل.


في TypeScript (Node.js) تتفاقم المشكلة بسبب عدم التزامن: لا تعرف أي الوعود (promises) لا تزال قيد التنفيذ. لكن هناك حل.



التقنية #1: الإنهاء المهذب (Graceful Shutdown)



أساس النشر بدون توقف هو المعالجة الصحيحة لإشارات نظام التشغيل. عندما يقرر المنظم (Kubernetes, PM2) إيقاف خدمتك، يرسل SIGTERM. يجب أن يتوقف كودك عن قبول الطلبات الجديدة، وينتظر اكتمال الطلبات الحالية، ثم يموت فقط.



التنفيذ بلغة TypeScript مع Express



import express, { Request, Response } from 'express';import http from 'http';

const app = express();let server: http.Server;

app.get('/health', (req: Request, res: Response) => { res.status(200).json({ status: 'ok' });});

// إشارة من Docker/K8sprocess.on('SIGTERM', async () => { console.log('تم استلام SIGTERM، بدء الإنهاء المهذب...'); // 1. التوقف عن الاستماع للاتصالات الجديدة server.close(() => { console.log('تم إغلاق خادم HTTP'); // 2. إغلاق الاتصالات بقاعدة البيانات، Redis إلخ. // await db.disconnect(); process.exit(0); });

// 3. إعطاء وقت للطلبات الحالية (مهلة 30 ثانية) setTimeout(() => { console.error('إنهاء إجباري بسبب انتهاء المهلة'); process.exit(1); }, 30000);});

const PORT = process.env.PORT || 3000;server = app.listen(PORT, () => { console.log(`الخدمة تعمل على المنفذ ${PORT}`);});


هام: في TypeScript استخدم http.Server من الوحدة http، وليس نوع Express. طريقة server.close() تعالج اتصالات keep-alive بشكل صحيح.



التقنية #2: التحديث المتدرج (Rolling Update)



إذا كان لديك عدة نسخ من الخدمة (3-5 مثيلات قيد التشغيل)، فإن التحديث المتدرج يقوم بتحديثها واحدة تلو الأخرى. بينما يتم إعادة تشغيل واحدة، تستقبل الأخرى حركة المرور.



مثال لـ Kubernetes Deployment



# deployment.yamlapiVersion: apps/v1kind: Deploymentmetadata:  name: my-typescript-servicespec:  replicas: 3  strategy:    type: RollingUpdate    rollingUpdate:      maxUnavailable: 1      # كم عدد البودات التي يمكن أن تكون غير متاحة      maxSurge: 1            # كم عدد البودات الجديدة التي يمكن إنشاؤها فوق الخطة  template:    spec:      containers:      - name: app        image: myregistry/service:v2        ports:        - containerPort: 3000        livenessProbe:          httpGet:            path: /health            port: 3000          initialDelaySeconds: 5          periodSeconds: 10        readinessProbe:          httpGet:            path: /ready            port: 3000          initialDelaySeconds: 3          periodSeconds: 5


أضف إلى تطبيق TypeScript الخاص بك نقطة نهاية /ready التي ترجع 200 فقط عندما تكون الخدمة جاهزة لاستقبال حركة المرور.

المدونات

توصيات الكتب