كانت إشعاراتنا جحيمًا من الاستعلامات المتكررة (Polling): كيف أنقذتنا WebSockets من استنزاف الخوادم؟

يا هلا والله فيكم يا جماعة الخير، معكم أخوكم أبو عمر.

خلوني أحكيلكم قصة صارت معي قبل كم سنة، قصة علّمتني درس قاسي عن أهمية اختيار الأداة الصح للشغل الصح. كنا وقتها بنشتغل على تطبيق اجتماعي جديد، والكل كان متحمس. واحدة من أهم الميزات كانت الإشعارات اللحظية: لما حدا يعجب بمنشورك، أو يكتب تعليق، أو يبعتلك رسالة، يوصلك إشعار فوري. إشي بسيط وحلو، صح؟

في البداية، وبكل سذاجة المبتدئين، قررنا نستخدم أسهل طريقة تخطر على بال أي مبرمج: الـ Polling. كتبنا كود بسيط في الواجهة الأمامية (Frontend) يسأل الخادم (Backend) كل 3 ثواني: “يا خادم، في إشي جديد؟” “يا خادم، صار إشي؟” “هسا في إشي؟”…

في أول أسبوعين، والأمور تمام واللوز. المستخدمين قلال والتطبيق طاير. لكن يوم الإطلاق الرسمي، ومع تزايد أعداد المستخدمين… بلّشت المصايب. الخوادم صارت تصرخ وتستنجد! المعالجات (CPU) وصلت 100%، وقاعدة البيانات يا حرام صارت أبطأ من سلحفاة طالعة مطلع. فجأة، لقيت مدير المشروع واقف فوق راسي وعيونه حمر وبيسألني بنبرة ما بتمزح: “أبو عمر، شو السيرة؟ التطبيق واقع والناس بتشكي!”.

كانت تلك الليلة جحيمًا بكل ما تحمله الكلمة من معنى. آلاف المستخدمين، كل واحد فيهم بيرسل طلب للخادم كل 3 ثواني، سواء في جديد أو ما في. كانت حرب استنزاف خسرانة ضد خوادمنا. وهون كانت بداية رحلتنا للبحث عن المنقذ، اللي كان اسمه WebSockets.

الفصل الأول: الجحيم الذي يُدعى “Polling”

عشان نكون على نفس الصفحة، خلونا نفهم بالزبط شو هو الـ Polling وليش كان كارثة بالنسبة إلنا.

ما هو الـ Polling (الاستعلام الدوري)؟

ببساطة شديدة، الـ Polling هو أن يقوم العميل (المتصفح أو تطبيق الموبايل) بإرسال طلب HTTP إلى الخادم بشكل متكرر على فترات زمنية ثابتة (مثلاً كل 5 ثواني) ليسأل “هل هناك بيانات جديدة؟”. الخادم يفحص، إذا وجد شيئًا جديدًا يرسله، وإذا لم يجد، يرد باستجابة فارغة.

تخيل أنك تنتظر مكالمة هاتفية مهمة، وبدل ما تنتظر الهاتف يرن، بتقوم أنت بالاتصال على الشخص كل دقيقة وتسأله: “بدك تحكي معي؟”. عملية مرهقة وغير فعّالة بالمرة!

في عالم جافاسكريبت، كان شكل الكود المأساوي كالتالي:


// Polling a-la-Jahannam (طريقة الجحيم)
setInterval(async () => {
  try {
    const response = await fetch('/api/notifications');
    const data = await response.json();

    if (data.newNotifications.length > 0) {
      // إذا في إشعارات جديدة، أظهرها للمستخدم
      displayNotifications(data.newNotifications);
    }
    // إذا ما في... ولا إشي، مجرد طلب ضائع
  } catch (error) {
    console.error("مشكلة في جلب الإشعارات:", error);
  }
}, 3000); // اسأل الخادم كل 3 ثواني! يا للهول!

لماذا الـ Polling سيء جدًا للتطبيقات اللحظية؟

  • استنزاف موارد الخادم: تخيل 10,000 مستخدم نشط. كل واحد يرسل طلب كل 3 ثواني. هذا يعني 3,333 طلبًا في الثانية! 99% من هذه الطلبات ستكون بلا فائدة، ولكنها تستهلك CPU، وذاكرة، وتفتح وتغلق اتصالات الشبكة، وتضغط على قاعدة البيانات.
  • استنزاف موارد العميل: في تطبيقات الموبايل، هذه الطلبات المتكررة تستنزف البطارية بشكل كبير. وفي المتصفح، تستهلك بيانات الإنترنت والمعالج.
  • ليست لحظية بما يكفي (High Latency): لو حددت الفاصل الزمني 5 ثواني، فقد ينتظر المستخدم 4.9 ثانية كاملة قبل أن يصله إشعار حدث للتو. هذه ليست “لحظية”. وإذا قللت الفاصل الزمني لثانية واحدة، فأنت تزيد الطين بلة وتضاعف العبء على الخادم.
  • إهدار في حجم البيانات (Bandwidth): كل طلب HTTP يأتي مع “ترويسات” (Headers) كثيرة. حتى لو كانت الاستجابة فارغة، أنت ترسل وتستقبل بيانات غير ضرورية في كل مرة.

نصيحة من أبو عمر: الـ Polling ليس شرًا مطلقًا. قد يكون مفيدًا في حالات نادرة جدًا لا يوجد فيها بديل، أو عندما يكون تحديث البيانات غير مهم أن يكون لحظيًا والفاصل الزمني طويل جدًا (مثلاً، التحقق من وجود تحديث للتطبيق مرة كل 24 ساعة). لكن للتطبيقات التفاعلية؟ ابتعد عنه كما تبتعد عن كود لا يوجد فيه تعليقات!

الفصل الثاني: محاولة التحسين الفاشلة (Long Polling)

بعد ما أدركنا حجم الكارثة، بحثنا عن حلول وسط. واحد من الحلول اللي ظهرت هو “Long Polling” أو الاستعلام الطويل. قلنا نجربه، بلكي ربنا فرجها.

كيف يعمل الـ Long Polling؟

الفكرة أذكى شوي من أختها الغبية (Short Polling). هنا، العميل يرسل طلبًا للخادم، ولكن الخادم لا يرد فورًا. بل يحتفظ بالطلب مفتوحًا وينتظر.

  • إذا حدث شيء جديد (وصل إشعار)، يقوم الخادم بالرد فورًا ومعه البيانات الجديدة.
  • إذا لم يحدث شيء خلال فترة زمنية معينة (مثلاً 30 ثانية)، ينتهي وقت الطلب (Timeout) ويرد الخادم باستجابة فارغة.

وبمجرد أن يستلم العميل الرد (سواء ببيانات أو فارغًا)، يقوم فورًا بإرسال طلب Long Polling جديد.

هذا يقلل من عدد الطلبات الفارغة بشكل كبير، ولكنه لا يزال… حلاً مؤقتًا.

العيوب التي بقيت

صحيح أن الـ Long Polling أفضل من الـ Polling العادي، لكنه لا يزال يعاني من مشاكل جوهرية. هو بمثابة “ترقيع” لبروتوكول HTTP الذي لم يُصمم لهذا النوع من الاتصالات. لا يزال هناك تعقيد في إدارة الاتصالات، والـ Timeouts، ولا يزال يستهلك موارد على الخادم لإبقاء هذه الطلبات “معلقة”. كان تحسنًا، لكنه لم يكن الحل الجذري الذي نبحث عنه.

الفصل الثالث: المنقذ WebSockets

وهنا وصلنا إلى بطل قصتنا. بعد بحث معمق، وجدنا تقنية اسمها WebSockets. في البداية، الاسم كان غريبًا ومخيفًا بعض الشيء، لكن بعد أن فهمنا مبدأ عملها، أدركنا أنها بالضبط ما نحتاجه.

ما هي الـ WebSockets؟

تخيل أنك بدل ما تتصل بصديقك كل شوي لتسأله عن الأخبار، فتحت معه خط ” рация” (Walkie-talkie) مفتوح طول الوقت. أي واحد فيكم بده يحكي، ببساطة بضغط على الزر وبيتكلم، والثاني بيسمعه فورًا. هذا هو بالزبط مبدأ عمل الـ WebSockets.

الـ WebSocket هو بروتوكول يوفر قناة اتصال ثنائية الاتجاه (Full-duplex) ومستمرة بين العميل والخادم عبر اتصال TCP واحد. العملية تبدأ بطلب HTTP عادي يسمى “Handshake” (مصافحة)، يطلب فيه العميل من الخادم “ترقية” الاتصال من HTTP إلى WebSocket. إذا وافق الخادم، يتم فتح هذه القناة السحرية، ويخرج بروتوكول HTTP من الصورة تمامًا.

الآن، يمكن للخادم أن يدفع (Push) البيانات إلى العميل في أي وقت، دون أن يطلب العميل ذلك. ويمكن للعميل أيضًا أن يرسل بيانات إلى الخادم في أي وقت عبر نفس القناة. لا مزيد من “هل هناك جديد؟”، بل أصبح الخادم هو من يخبرنا “هيه، خذ هذا الإشعار الجديد!”.

مثال عملي: من Polling إلى WebSockets

لنجعل الأمور عملية. لنرى كيف تحول الكود من الجحيم إلى النعيم.

كود العميل (Client-side JavaScript)


// إنشاء اتصال WebSocket جديد مع الخادم
// لاحظ استخدام wss:// للاتصال الآمن، مثل https://
const socket = new WebSocket('wss://our-awesome-app.com/socket');

// الاستماع لحدث فتح الاتصال بنجاح
socket.onopen = (event) => {
  console.log('تم الاتصال بالخادم بنجاح! يا هلا!');
  // ممكن نرسل بيانات تعريفية للمستخدم هنا
  socket.send(JSON.stringify({ type: 'auth', token: 'user-jwt-token' }));
};

// الاستماع للرسائل القادمة من الخادم
// هنا يكمن السحر كله!
socket.onmessage = (event) => {
  const message = JSON.parse(event.data);

  // الخادم أرسل لنا إشعارًا جديدًا
  if (message.type === 'new_notification') {
    displayNotifications(message.payload);
  }
};

// الاستماع لحدث إغلاق الاتصال
socket.onclose = (event) => {
  console.log('انقطع الاتصال، سنحاول إعادة الاتصال...');
  // هنا يجب وضع منطق لإعادة الاتصال
};

// الاستماع لأي أخطاء
socket.onerror = (error) => {
  console.error('حدث خطأ في الـ WebSocket:', error);
};

كود الخادم (Server-side – مثال باستخدام Node.js ومكتبة `ws`)


import { WebSocketServer } from 'ws';

const wss = new WebSocketServer({ port: 8080 });

// هذا الكود يتم تشغيله لكل عميل يتصل
wss.on('connection', ws => {
  console.log('عميل جديد اتصل.');

  // الاستماع للرسائل من هذا العميل المحدد
  ws.on('message', message => {
    console.log('استقبلنا رسالة:', message);
    // هنا يتم معالجة الرسائل، مثل التحقق من التوكن
  });

  ws.on('close', () => {
    console.log('عميل قطع الاتصال.');
  });

  // مثال: كيف يرسل الخادم إشعارًا؟
  // عندما يحدث شيء في مكان آخر في التطبيق (مثلاً، تعليق جديد)
  // نستدعي دالة مثل هذه
  function sendNotificationToUser(userId, notification) {
    // نبحث عن اتصال الـ WebSocket الخاص بهذا المستخدم ونرسل له
    // (هذا يتطلب إدارة الاتصالات وربطها بالمستخدمين)
    const userSocket = findSocketByUserId(userId);
    if (userSocket) {
      userSocket.send(JSON.stringify({
        type: 'new_notification',
        payload: notification
      }));
    }
  }
});

النتائج المذهلة

بمجرد أن طبقنا الـ WebSockets، كانت النتائج فورية ومدهشة:

  1. أداء الخادم: انخفض استهلاك الـ CPU من 100% في أوقات الذروة إلى أقل من 10%. أصبح الخادم يتنفس الصعداء.
  2. سرعة الإشعارات: أصبحت الإشعارات تصل بشكل فوري حقيقي. لا يوجد أي تأخير ملحوظ.
  3. استهلاك الموارد: انخفض استهلاك بيانات الإنترنت والبطارية لدى العملاء بشكل كبير.
  4. بساطة الكود (على المدى الطويل): رغم أن الإعداد الأولي قد يكون أعقد بقليل من `setInterval` سخيفة، إلا أن إدارة الاتصالات اللحظية أصبحت أكثر تنظيمًا ومركزية.

نصيحة من أبو عمر: لا تخف من البدء مباشرة مع WebSockets. في أيامنا هذه، هناك مكتبات رائعة مثل Socket.IO (لـ JavaScript) و SignalR (لـ .NET) و غيرها الكثير لمختلف اللغات، التي تبسط العملية بشكل كبير. هي لا تدير فقط اتصال الـ WebSocket، بل توفر آليات “fallback” تلقائية إلى Long Polling في حال كان المتصفح أو الشبكة لا تدعم WebSockets، وتعالج إعادة الاتصال تلقائيًا. أنت تحصل على أفضل ما في العالمين دون عناء.

الخلاصة: اختر الأداة المناسبة للمعركة

رحلتنا من جحيم الـ Polling إلى نعيم الـ WebSockets كانت درسًا مهمًا في هندسة البرمجيات. تعلمنا بالطريقة الصعبة أن الحلول السهلة والسريعة في البداية قد تتحول إلى كوابيس تقنية مع نمو التطبيق. بروتوكول HTTP عظيم لنموذج الطلب والاستجابة (Request/Response)، لكنه الأداة الخاطئة تمامًا عندما تحتاج إلى اتصالات مستمرة ولحظية.

عندما تبني تطبيقك القادم الذي يحتاج إلى أي شكل من أشكال التفاعل اللحظي – سواء كانت تطبيقات دردشة، ألعاب أونلاين، لوحات تحكم حية، منصات تداول، أو حتى إشعارات بسيطة – فكر مباشرة في الـ WebSockets. ستشكر نفسك (وفريقك، وخوادمك) لاحقًا على هذا القرار.

يلا يا جماعة، شدوا حيلكم وخلوا تطبيقاتكم سريعة زي البرق! 🚀

أبو عمر

سجل دخولك لعمل نقاش تفاعلي

كافة المحادثات خاصة ولا يتم عرضها على الموقع نهائياً

آراء من النقاشات

لا توجد آراء منشورة بعد. كن أول من يشارك رأيه!

آخر المدونات

الحوسبة السحابية

كانت أسرارنا مكشوفة في الكود: كيف أنقذنا ‘مدير الأسرار السحابي’ من جحيم التسريبات المحتملة؟

في هذه المقالة، يشارك أبو عمر قصة حقيقية عن اكتشاف أسرار حساسة مكتوبة في الكود، وكيف كانت هذه بداية الرحلة نحو استخدام "مدير الأسرار السحابي"...

30 مايو، 2026 قراءة المزيد
التوظيف وبناء الهوية التقنية

معرض أعمالي كان مقبرة لتطبيقات ‘المهام’: كيف أنقذني ‘المشروع التميزي’ من جحيم التشابه؟

كنت أظن أن كثرة المشاريع هي مفتاح الوظيفة، حتى اكتشفت أن معرض أعمالي مجرد مقبرة لتطبيقات مكررة. في هذه المقالة، أسرد لكم كيف أنقذني التركيز...

30 مايو، 2026 قراءة المزيد
التوسع والأداء العالي والأحمال

كانت ذروة المبيعات تقتل خوادمنا: كيف أنقذتنا ‘طوابير الرسائل’ من جحيم الطلبات المفقودة؟

أشارككم قصة حقيقية من قلب المعركة مع الأحمال العالية في موسم التخفيضات، وكيف كانت "طوابير الرسائل" (Message Queues) هي طوق النجاة الذي أنقذ تطبيقنا من...

29 مايو، 2026 قراءة المزيد
التكنلوجيا المالية Fintech

من الصندوق الأسود إلى الشفافية: كيف فتحنا أبواب الثقة في التقييم الائتماني باستخدام XAI

التقييم الائتماني كان صندوقاً أسود غامضاً، يرفض الطلبات دون تفسير. في هذه المقالة، أسرد لكم قصة حقيقية من تجربتي كـ "أبو عمر" عن كيفية استخدامنا...

29 مايو، 2026 قراءة المزيد
البنية التحتية وإدارة السيرفرات

كانت بيئاتنا نسخاً مشوهة: كيف أنقذتنا ‘البنية التحتية كوداً’ (IaC) من جحيم الانحراف التكويني؟

أشارككم قصة من قلب المعركة التقنية، عن ليلة كادت أن تودي بمشروع كامل بسبب "الانحراف التكويني". اكتشفوا كيف أصبحت "البنية التحتية كوداً" (IaC) وأدوات مثل...

29 مايو، 2026 قراءة المزيد
أدوات وانتاجية

كانت واجهة الأوامر تبطئني: كيف أنقذني ‘الباحث التقريبي’ (Fuzzy Finder) من جحيم البحث عن الملفات والأوامر؟

كنت أقضي دقائق ثمينة في البحث عن ملفات وأوامر قديمة في واجهة الأوامر، مما كان يقتل إنتاجيتي. في هذه المقالة، أشارككم قصتي مع أداة 'الباحث...

29 مايو، 2026 قراءة المزيد
البودكاست