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

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

قبل كم سنة، كنت شغال مع فريق على نظام متجر إلكتروني كبير. كان واحد من التحديات الرئيسية هو مزامنة حالة الطلبات مع نظام إدارة المخزون وشحن البضائع، وهو نظام تابع لشركة ثانية (Third-party). يعني لما الزبون يشتري غرض، لازم نبعث خبر لشركة الشحن، ولما شركة الشحن تجهّز الطلبية أو تشحنها، لازم نعرف فوراً عشان نحدّث حالة الطلب عند الزبون ونبعتله إشعار.

في البداية، شو عملنا؟ الحل السريع والسهل اللي خطر ببالنا كان الـ “Polling”. كتبنا سكربت بسيط، كل ثانية يروح يسأل الـ API تبع شركة الشحن: “يا جماعة، في إشي جديد بخصوص الطلب رقم 123؟”، “طيب والطلب 124؟”، “طيب و125؟”. وهكذا دواليك لكل الطلبات المفتوحة. في أول يومين ثلاثة، كانت الأمور تمام والوضع “عال العال”.

لكن مع زيادة الطلبات، بدأت المصايب تظهر. الخادم (السيرفر) تبعنا صار زي فرن الخبز من كثر الضغط، استهلاك المعالج والذاكرة وصل للسما. وفوق كل هاد، شركة الشحن بعتتلنا إيميل محترم بيقول: “يا عمي خفّوا علينا شوي، عملتوا عنا مليون طلب API بيوم واحد! إذا بتضلوا هيك رح نعمللكم Block”. وقتها حسيت الدنيا ولّعت فوق راسنا. كنا في جحيم حقيقي اسمه “الاستطلاع المستمر”، وكنا بنحرق مواردنا وموارد غيرنا على الفاضي.

هنا كانت نقطة التحول. بعد بحث ونقاش، قررنا نتبنى الحل الأمثل والأكثر احترافية: خطافات الويب (Webhooks). ومن يومها، تغير كل شيء. خلوني أحكيلكم القصة بالتفصيل التقني، وكيف ممكن تستفيدوا من تجربتنا.

ما هو الاستطلاع المستمر (Polling)؟ أو “طريقة دق الباب كل شوي”

ببساطة شديدة، الـ Polling هو إنك كـ “عميل” (Client)، تضل تسأل الـ “خادم” (Server) بشكل متكرر إذا كان في أي تحديثات جديدة. تخيل حالك مستني صاحبك يجهز عشان تطلعوا، فبتروح كل دقيقة تدق عليه الباب وتسأله: “خلصت؟”. في معظم المرات، رح يكون جوابه “لسا”، وهيك بتكون ضيعت وقتك ومجهودك في المشوار لعنده.

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

الآلية بسيطة جداً وتتلخص في حلقة لا نهائية:

  1. العميل يرسل طلب HTTP (عادةً GET) للخادم: “هل هناك أي بيانات جديدة؟”
  2. الخادم يفحص قاعدة بياناته أو حالته الداخلية ويرد. إما ببيانات جديدة، أو برد فارغ مثل {"updates": false}.
  3. العميل ينتظر فترة زمنية محددة (مثلاً، ثانية واحدة أو 5 ثوانٍ).
  4. يعود العميل إلى الخطوة 1.

الكود يتحدث: مثال بسيط على الـ Polling بـ JavaScript

هذا مثال بسيط يوضح الفكرة باستخدام JavaScript في المتصفح. الكود يحاول جلب تحديثات كل 3 ثوانٍ.


// The ID of the order we are tracking
const orderId = 'xyz-123';

// Function to check for updates
async function checkOrderStatus() {
  try {
    const response = await fetch(`https://api.example.com/orders/${orderId}/status`);
    if (!response.ok) {
      console.error('Network response was not ok');
      return;
    }
    const data = await response.json();

    // If there is a new status, update the UI
    if (data.new_status) {
      console.log('New status received:', data.status);
      // updateUI(data.status);
    } else {
      console.log('No new updates yet...');
    }
  } catch (error) {
    console.error('Failed to fetch status:', error);
  }
}

// Poll every 3 seconds
setInterval(checkOrderStatus, 3000);

الكود واضح، لكن تخيل آلاف العملاء يقومون بهذا الإجراء في نفس الوقت. إنها وصفة لكارثة في الأداء.

جحيم الـ Polling: لماذا هو فكرة سيئة في معظم الأحيان؟

تجربتنا المريرة مع الـ Polling لخصت لنا عيوبه القاتلة:

  • استهلاك فظيع للموارد: كل طلب استطلاع يستهلك موارد الشبكة، المعالج، والذاكرة على الخادم والعميل، حتى لو لم يكن هناك أي تحديث. 99% من الطلبات كانت تعود فارغة، وهذا هدر محض.
  • التأخير الحتمي (Latency): التحديثات ليست فورية. إذا كان الفاصل الزمني للاستطلاع 5 ثوانٍ، فقد ينتظر المستخدم 4.9 ثانية كاملة بعد حدوث التغيير الفعلي قبل أن يراه.
  • مشاكل التوسع (Scalability): كلما زاد عدد العملاء، زاد الحمل على الخادم بشكل خطي. إذا كان لديك مليون مستخدم، فهذا يعني ملايين الطلبات كل بضع ثوانٍ. هذا لا يمكن أن يستمر.
  • قيود الاستخدام (Rate Limiting): معظم واجهات برمجة التطبيقات (APIs) تفرض حداً على عدد الطلبات التي يمكنك إجراؤها في الدقيقة أو الساعة. مع الـ Polling، من السهل جداً تجاوز هذا الحد والتعرض للحظر، زي ما كان رح يصير معنا.

المنقذ: خطافات الويب (Webhooks) أو “لما تجهز، رن عليّ”

الـ Webhook يقلب المعادلة رأساً على عقب. بدلاً من أن يسأل العميل الخادم باستمرار، يقوم الخادم بإخبار العميل عند وقوع حدث معين. نرجع لمثال صاحبك: بدل ما تضل تدق الباب عليه، بتعطيه رقم تلفونك وبتقوله: “لما تجهز، ابعثلي رسالة أو رن عليّ”.

بالمصطلح التقني، الـ Webhook هو “رد نداء HTTP” (HTTP callback) أو “HTTP Push API”. أنت تقوم بتزويد الخدمة (مثل بوابة الدفع، أو نظام الشحن) بعنوان URL خاص بتطبيقك. وعندما يحدث شيء يهمك (مثل إتمام عملية دفع، أو تغيير حالة الشحنة)، تقوم تلك الخدمة بإرسال طلب HTTP POST إلى عنوانك، محمّلاً بالبيانات ذات الصلة.

كيف تعمل الـ Webhooks؟

  1. الإعداد (مرة واحدة): أنت (العميل) تدخل على لوحة تحكم الخدمة (الخادم) وتسجل عنوان URL خاص بك، يسمى “Webhook Endpoint”. هذا الـ Endpoint هو جزء من الكود تبعك ومستعد لاستقبال البيانات.
  2. وقوع الحدث: يحدث شيء على الخادم (مثلاً، تم شحن طلبية).
  3. الإشعار: الخادم يقوم فوراً بإنشاء طلب HTTP POST، يضع فيه تفاصيل الحدث (مثلاً، بيانات الطلب وحالته الجديدة) في جسم الطلب (Body) بصيغة JSON، ويرسله إلى الـ URL الذي سجلته.
  4. المعالجة: تطبيقك يستقبل هذا الطلب، يتأكد من صحته، ثم يقوم بالإجراء اللازم (تحديث قاعدة البيانات، إرسال إيميل للزبون، إلخ).

الكود يتحدث: إعداد نقطة نهاية (Endpoint) لاستقبال Webhook

هذا مثال بسيط باستخدام Node.js و Express لإنشاء خادم يستمع لطلبات الـ Webhooks القادمة.


const express = require('express');
const app = express();

// Middleware to parse JSON bodies. This is crucial!
app.use(express.json());

const PORT = 3000;

// This is our Webhook Endpoint
app.post('/webhooks/shipping-updates', (req, res) => {
  console.log('Webhook received!');
  
  // The data sent by the third-party service is in the request body
  const payload = req.body;
  
  console.log('Payload:', payload);
  
  // Example payload: { orderId: 'xyz-123', newStatus: 'shipped', trackingNumber: '...' }
  const { orderId, newStatus } = payload;
  
  // Now, do something with the data
  // e.g., update your database, notify the user, etc.
  // updateOrderStatusInDB(orderId, newStatus);
  
  console.log(`Order ${orderId} status updated to ${newStatus}`);
  
  // It's important to send a 200 OK response back quickly
  // to let the sender know you've received the webhook successfully.
  res.status(200).send('Webhook received');
});

app.listen(PORT, () => {
  console.log(`Server listening for webhooks on port ${PORT}`);
});

نصيحة من أبو عمر: تأمين الـ Webhooks

يا جماعة، انتبهوا لنقطة مهمة جداً. الـ Endpoint تبع الـ Webhook هو عبارة عن URL عام على الإنترنت. أي حدا بيعرفهبيقدر يبعتلك عليه طلبات مزيفة. لهذا السبب، يجب تأمينه. الطريقة الأكثر شيوعاً هي “التحقق من التوقيع” (Signature Verification).

الفكرة كالتالي: عند إعداد الـ Webhook، تقوم الخدمة بإنشاء “سر مشترك” (Secret) بينك وبينها. عند إرسال أي Webhook، تقوم الخدمة بحساب “تجزئة” (Hash) للبيانات المرسلة (الـ payload) باستخدام هذا السر، وترسل لك نتيجة الـ Hash في هيدر خاص (مثلاً X-Hub-Signature-256).
مهمتك أنت في الكود تبعك هي أن تقوم بنفس العملية: تستقبل الطلب، تأخذ الـ payload والـ Secret، تحسب الـ Hash بنفسك، وتقارنه مع الـ Hash اللي وصلك في الهيدر. إذا تطابقوا، فالطلب حقيقي. إذا لم يتطابقوا، فهذا طلب مزيف ويجب تجاهله.

مقارنة وجهاً لوجه: Polling vs. Webhooks

المعيار الاستطلاع المستمر (Polling) خطافات الويب (Webhooks)
الكفاءة منخفضة جداً، معظم الطلبات تكون هدر للموارد. عالية جداً، لا يتم إرسال طلبات إلا عند وجود تحديث فعلي.
الفورية (Real-time) يوجد تأخير يعتمد على الفاصل الزمني للاستطلاع. فوري تقريباً، يتم الإشعار لحظة وقوع الحدث.
بنية الاتصال العميل يبدأ الاتصال (Pull). الخادم يبدأ الاتصال (Push).
التعقيد في الإعداد بسيط جداً في البداية. أكثر تعقيداً، يتطلب نقطة نهاية عامة (Public Endpoint) وتأمين.
قابلية التوسع ضعيفة جداً. ممتازة.

نصائح عملية من مطبخ أبو عمر 🍳

بعد سنوات من التعامل مع الـ Webhooks، تعلمت بعض الدروس اللي بحب أشارككم فيها:

  • اجعل معالج الـ Webhook سريعاً جداً: لا تقم بعمليات طويلة أو معقدة مباشرة عند استقبال الـ Webhook. مهمة الـ Endpoint هي استقبال الطلب، التحقق من صحته، ثم تمرير المهمة لعملية أخرى في الخلفية (Background Job). أفضل ممارسة هي وضع بيانات الـ Webhook في طابور (Message Queue) مثل RabbitMQ أو AWS SQS، ثم إرجاع استجابة 200 OK فوراً. هذا يمنع حدوث Timeouts من جهة المرسل.
  • كن مستعداً لإعادة الإرسال: أحياناً قد يكون الخادم تبعك معطلاً أو مشغولاً عند وصول الـ Webhook. الخدمات المحترمة تعيد إرسال الـ Webhook عدة مرات (Retry Mechanism). هذا يعني أن الكود تبعك يجب أن يكون “Idempotent”، أي أن معالجة نفس الحدث مرتين أو ثلاث لا تسبب مشكلة (مثلاً، لا ترسل للزبون إيميل “تم شحن الطلب” مرتين). يمكنك تحقيق ذلك عن طريق التحقق من معرف الحدث (Event ID) قبل معالجته.
  • استخدم أدوات للاختبار المحلي: أثناء التطوير، يكون الخادم المحلي (localhost) الخاص بك غير متاح على الإنترنت. كيف ستستقبل الـ Webhooks؟ استخدم أدوات مثل ngrok التي تنشئ نفقاً آمناً من الإنترنت إلى جهازك المحلي. إنها أداة لا غنى عنها.
  • التسجيل والمراقبة (Logging & Monitoring): سجل كل طلب Webhook يصلك (مع إخفاء البيانات الحساسة طبعاً). هذا سيساعدك بشكل لا يصدق في تصحيح الأخطاء عندما تسوء الأمور. “ليش ما تحدثت حالة الطلب؟”، الجواب غالباً ما يكون في سجلات الـ Webhooks.

الخلاصة: متى تستخدم هذا أو ذاك؟ 🤔

الخلاصة بسيطة ومباشرة:

👈 استخدم الـ Polling (بحذر شديد): عندما تكون مضطراً، أي عندما لا تدعم الخدمة التي تتعامل معها الـ Webhooks، أو عندما لا تحتاج إطلاقاً لتحديثات فورية ومعدل التغيير منخفض جداً (مثلاً، التحقق من الطقس مرة كل ساعة). اعتبره دائماً حلاً مؤقتاً أو الخيار الأخير.

🚀 استخدم الـ Webhooks (كلما أمكن): إنها الخيار الأمثل للاتصالات الفورية بين الخوادم، والتطبيقات التي تعتمد على الأحداث (Event-Driven Architecture)، وأنظمة الإشعارات، وتكامل الخدمات. إنها الطريقة الحديثة والفعالة لبناء أنظمة متجاوبة وقابلة للتوسع.

في عالم البرمجة، الكفاءة ليست فقط كتابة كود يعمل، بل كتابة كود ذكي يحترم الموارد والوقت. والـ Webhooks هي واحدة من أذكى الأدوات في جعبتنا. فاستخدموها بحكمة، وخليكم مبدعين! 😉

أبو عمر

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

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

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

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

آخر المدونات

التوظيف وبناء الهوية التقنية

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

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

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

كانت واجهاتنا البرمجية مرتعاً للبوتات: كيف أنقذنا ‘تحديد المعدل’ (Rate Limiting) من جحيم الاستنزاف؟

أشارككم قصة حقيقية من الخنادق البرمجية، حين كادت خوادمنا أن تنهار تحت وطأة طلبات لا تنتهي. اكتشفوا كيف كان "تحديد المعدل" (Rate Limiting) هو طوق...

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

كانت بيانات عملائنا المصرفية سجينة: كيف أنقذتنا ‘المصرفية المفتوحة’ (Open Banking) من جحيم الأنظمة المغلقة؟

أشارككم قصة حقيقية من قلب المعركة البرمجية، كيف انتقلنا من كابوس "كشط الشاشة" والأنظمة المصرفية المغلقة إلى عالم من الإمكانيات بفضل ثورة المصرفية المفتوحة (Open...

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

كانت بنيتنا التحتية تُبنى بالنقرات والأدعية: كيف أنقذنا Terraform من جحيم الإعداد اليدوي؟

أشارككم قصة حقيقية عن كارثة كادت أن تدمر إطلاق منتج جديد بسبب الإعداد اليدوي للسيرفرات. اكتشفوا كيف انتقلنا من فوضى النقرات إلى عالم "البنية التحتية...

28 أبريل، 2026 قراءة المزيد
اختبارات الاداء والجودة

كانت تغطية اختباراتنا 100% لكنها كذبة: كيف أنقذنا “الاختبار الطفري” من جحيم الثقة الزائفة؟

كنا نظن أن تغطية اختبارات بنسبة 100% هي درعنا الواقي، لكنها كانت ثقة زائفة. أشارككم قصة كيف كشف "الاختبار الطفري" (Mutation Testing) ضعف اختباراتنا وأنقذ...

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

كانت بيئة التطوير لدينا كلاً في وادٍ: كيف أنقذتنا ‘حاويات التطوير’ (Dev Containers) من جحيم ‘إنها تعمل على جهازي’؟

أتذكر جيداً ذلك اليوم الذي كاد فيه مبرمج جديد أن يستقيل في أسبوعه الأول بسبب مشاكل إعداد البيئة. في هذه المقالة، أسرد لكم يا جماعة...

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