يا حيا الله الشباب، أبو عمر معكم.
قبل كم سنة، كنا شغالين على مشروع كبير لمتجر إلكتروني ضخم. كان جزء من الشغل هو مزامنة المخزون مع نظام المورّد الأساسي. كل ما تنباع قطعة عنا، لازم نحدّث الكمية، وكل ما المورّد يضيف بضاعة جديدة، لازم تظهر عنا فوراً. بسيطة، مش هيك؟ هكذا ظننا في البداية.
الطريقة “البديهية” اللي خطرت في بالنا كانت الاستقصاء المستمر أو الـ Polling. كتبنا سكربت بسيط، كل دقيقة يبعث طلب لواجهة برمجة التطبيقات (API) تاعت المورّد ويسأله: “يا حاج، في إشي جديد؟ شو صار بالمخزون؟”. في أول أسبوع، كانت الأمور “ماشية”. لكن مع زيادة عدد المنتجات والطلبات، بدأت الخوادم (السيرفرات) تئن وتصرخ. استهلاك المعالج (CPU) في السما، استجابة الموقع صارت بطيئة، وزبوننا بلّش يتذمر. والأسوأ من كل هاد، إنه التحديثات ما كانت فورية، كانت بتتأخر دايماً دقيقة كاملة على الأقل.
كنا حرفياً في جحيم تقني: خوادمنا تستجدي الرحمة من كثرة الطلبات الفارغة، والمستخدمين يشتكون من البطء، والمورّد بدأ يبعثلنا تحذيرات بسبب استهلاكنا المفرط لحصتنا من طلبات الـ API. شعرت وقتها كأنني أحاول ملء خزان ماء مثقوب باستخدام ملعقة صغيرة. إلى أن قرأت في توثيق الـ API تاعهم عن إشي اسمه “Webhooks” أو “خطاطيف الويب”. تلك اللحظة، يا جماعة، غيرت كل إشي.
ما هو الاستقصاء المستمر (Polling)؟ أو “الطريقة القديمة”
قبل ما ندخل في الحل السحري، خلونا نفهم المشكلة صح. الـ Polling هو ببساطة أن يقوم نظامك (العميل) بسؤال نظام آخر (الخادم) بشكل متكرر وعلى فترات زمنية ثابتة: “هل هناك أي تحديثات؟”.
تخيل حالك بتستنى مكالمة مهمة، فبتمسك التلفون كل 30 ثانية وبتسأل: “حدا رن؟… طيب هسا؟… طيب كمان مرة؟”. هذا بالضبط ما يفعله الـ Polling. العميل يظل يرسل طلبات HTTP GET إلى الخادم، منتظراً أي تغيير.
كيف يعمل الـ Polling؟
الآلية بسيطة جداً، وهذا سبب جاذبيتها للمبتدئين:
- العميل (تطبيقك) يبدأ حلقة تكرارية (loop) أو مؤقت زمني (timer).
- مع كل دورة، يرسل طلب
GETإلى نقطة نهاية (endpoint) محددة في الخادم. - الخادم يبحث عن أي بيانات جديدة منذ آخر طلب.
- الخادم يعيد استجابة، والتي في 99% من الحالات تكون “لا يوجد شيء جديد”.
- العميل ينتظر الفترة الزمنية المحددة (مثلاً، دقيقة واحدة) ويعيد الكرة.
// مثال بسيط جداً على Polling باستخدام جافاسكريبت
setInterval(async () => {
try {
const response = await fetch('https://api.supplier.com/inventory/updates');
const data = await response.json();
if (data.hasUpdates) {
console.log('تحديث جديد وصل!');
// ... ابدأ بمعالجة البيانات الجديدة
updateLocalInventory(data.updates);
} else {
console.log('لا جديد يذكر... سأعاود السؤال بعد قليل.');
}
} catch (error) {
console.error('حدث خطأ أثناء الاستقصاء:', error);
}
}, 60000); // السؤال كل 60 ثانية (دقيقة)
مساوئ الـ Polling: ليش هو “شغل مش مرتب”
على الرغم من بساطته، إلا أن هذا الأسلوب يأتي مع فاتورة باهظة:
- استهلاك الموارد (Resource Intensive): كل طلب يستهلك معالج وذاكرة وشبكة على الخادم والعميل. تخيل آلاف العملاء يقومون بذلك في نفس الوقت. إنها وصفة لكارثة في الأداء.
- التأخير (Latency): التحديثات ليست حقيقية (Real-time). إذا حدث شيء مهم بعد ثانية واحدة من آخر طلب، فسيتعين عليك الانتظار 59 ثانية أخرى لمعرفة ذلك. تقليل الفاصل الزمني يزيد من مشكلة استهلاك الموارد.
- مشاكل التوسع (Scalability Issues): كلما زاد عدد عملائك، زاد الحمل على الخادم بشكل خطي أو حتى أسوأ. يصبح من الصعب جداً توسيع نطاق النظام.
- استهلاك حدود الـ API (API Rate Limiting): معظم الخدمات تفرض حداً على عدد الطلبات التي يمكنك إجراؤها في الدقيقة أو الساعة. الـ Polling يستهلك هذه الحصة بسرعة، مما قد يؤدي إلى حظر تطبيقك مؤقتاً.
المنقذ: خطاطيف الويب (Webhooks) أو “الطريقة الذكية”
هنا يأتي دور البطل. الـ Webhooks تقلب المعادلة رأساً على عقب. بدلاً من أن تسأل أنت “هل هناك جديد؟”، يقوم الخادم بإخبارك بنفسه عندما يحدث شيء جديد. المبدأ هو: “لا تتصل بنا، نحن سنتصل بك”.
الـ Webhook هو في جوهره “API عكسي”. أنت لا تطلب بيانات، بل تستقبلها. تقوم بتزويد الخدمة الخارجية (مثل GitHub, Stripe, Shopify) بعنوان URL خاص على خادمك. وعندما يقع حدث معين في تلك الخدمة (مثل دفعة مالية ناجحة، طلب جديد، commit جديد في المستودع)، تقوم تلك الخدمة بإرسال طلب HTTP POST إلى عنوانك، محملًا بكافة تفاصيل الحدث.
آلية عمل الـ Webhooks: كيف بصير السحر؟
- التسجيل (Registration): في لوحة تحكم الخدمة الخارجية، تذهب إلى قسم الـ Webhooks وتضيف عنوان URL لنقطة نهاية على الخادم الخاص بك (مثلاً:
https://my-app.com/webhooks/supplier-updates). وتختار الأحداث التي تهمك (مثلاً:inventory.updated). - الحدث (Event): يقوم مستخدم بشراء منتج من متجرك على منصة المورد.
- الإشعار (Notification): نظام المورد يرى هذا الحدث، وبشكل فوري وتلقائي، يقوم بإنشاء طلب HTTP POST.
- الحمولة (Payload): يضع نظام المورد كل تفاصيل الحدث (رقم المنتج، الكمية الجديدة، الوقت، …) في جسم الطلب (request body)، غالباً بصيغة JSON.
- المعالجة (Processing): خادمك يستقبل هذا الطلب على الـ URL الذي سجلته، يقرأ البيانات من الـ Payload، ويقوم بالإجراء اللازم (مثلاً، تحديث قاعدة البيانات الخاصة بك).
// مثال على خادم Node.js مع Express يستقبل Webhook
const express = require('express');
const app = express();
// Middleware لقراءة جسم الطلب بصيغة JSON
app.use(express.json());
// نقطة النهاية التي تستقبل الـ Webhook
app.post('/webhooks/supplier-updates', (req, res) => {
const payload = req.body;
console.log('🎉 Webhook جديد وصل!');
console.log('الحدث:', payload.event_type); // مثلاً: "inventory.updated"
console.log('البيانات:', payload.data);
// هنا يتم منطق معالجة البيانات
// مثلاً، تحديث قاعدة البيانات بناءً على payload.data
updateLocalInventory(payload.data);
// الأهم: أرسل استجابة نجاح فوراً!
res.status(200).send('OK');
});
app.listen(3000, () => console.log('الخادم يستمع على المنفذ 3000'));
مقارنة وجهاً لوجه: Polling ضد Webhooks
“الـ Polling مثل طفل في المقعد الخلفي للسيارة يسأل كل دقيقة: “هل وصلنا بعد؟”. أما الـ Webhooks فهي مثل نظام الملاحة GPS الذي يخبرك فقط عند الحاجة إلى الانعطاف.”
- الكفاءة:
- Polling: منخفضة جداً. الكثير من الطلبات الفارغة والمهدرة.
- Webhooks: عالية جداً. لا يتم إرسال أي طلب إلا عند وجود معلومات حقيقية.
- سرعة التحديث:
- Polling: تعتمد على الفاصل الزمني. دائماً هناك تأخير.
- Webhooks: فورية تقريباً (Real-time). التحديث يصل في نفس اللحظة التي يقع فيها الحدث.
- بنية النظام:
- Polling: بسيطة من جهة العميل، ولكنها تضع حملاً هائلاً على الخادم المصدر.
- Webhooks: تتطلب إعداد نقطة نهاية عامة يمكن الوصول إليها (publicly accessible URL)، ولكنها تخفف الحمل بشكل كبير عن كلا النظامين.
نصائح من خبرة أبو عمر: كيف تطبق الـ Webhooks صح؟
تطبيق الـ Webhooks يبدو سهلاً، لكن هناك بعض التفاصيل المهمة اللي بتفرق بين الشغل المرتب والشغل اللي بيعمل مشاكل. خذوا هالنصائح من القلب:
1. الاستجابة السريعة هي مفتاح النجاح
الخدمة التي ترسل الـ Webhook تنتظر منك رداً سريعاً (عادة في غضون ثوانٍ قليلة). إذا تأخرت في الرد، قد تعتبر الخدمة أن الإرسال فشل وتحاول مرة أخرى، مما قد يؤدي إلى معالجة نفس الحدث مرتين. القاعدة الذهبية: يجب أن يكون معالج الـ Webhook الخاص بك سريعاً جداً.
الحل: لا تقم بالعمليات المعقدة أو التي تستغرق وقتاً طويلاً (مثل إرسال بريد إلكتروني، معالجة صور، استدعاء APIs أخرى) مباشرةً داخل دالة معالجة الـ Webhook. بدلاً من ذلك، قم بوضع المهمة في طابور انتظار (Message Queue) مثل RabbitMQ, SQS, أو حتى جدول بسيط في قاعدة البيانات، ثم أرسل استجابة 200 OK فوراً. دع عاملاً منفصلاً (Worker) يأخذ المهام من الطابور وينفذها بهدوء.
2. الأمان أولاً يا جماعة!
نقطة نهاية الـ Webhook الخاصة بك هي عنوان URL عام على الإنترنت. هذا يعني أن أي شخص يعرف العنوان يمكنه إرسال طلبات POST إليه. كيف تتأكد من أن الطلب الذي وصلك هو من الخدمة الحقيقية وليس من مخترق يحاول إفساد بياناتك؟
الحل: التحقق من التوقيع (Signature Verification). معظم الخدمات المحترمة (مثل Stripe و GitHub) تقوم بتوقيع طلبات الـ Webhook. عند إعداد الـ Webhook، ستحصل على “سر” (secret). الخدمة تستخدم هذا السر لإنشاء توقيع هاش (HMAC) لجسم الطلب وتضعه في هيدر (header) خاص مثل X-Hub-Signature-256. عند استقبال الطلب، تقوم أنت بنفس العملية: تحسب الهاش لجسم الطلب باستخدام نفس السر، وتقارنه بالتوقيع الموجود في الهيدر. إذا تطابقا، فالطلب أصلي. إذا لم يتطابقا، فتجاهله تماماً.
3. كن مستعداً للفشل (وهذا طبيعي)
ماذا لو كان خادمك متوقفاً عن العمل للصيانة أو لأي سبب آخر عند محاولة الخدمة إرسال Webhook؟ معظم الخدمات لديها آلية إعادة محاولة (retry mechanism)، غالباً مع تباعد زمني متزايد (exponential backoff). هذا يعني أيضاً أنك قد تستقبل نفس الـ Webhook أكثر من مرة.
الحل: اجعل نقطة النهاية الخاصة بك “متكررة المفعول” (Idempotent). هذا مصطلح فخم يعني أن معالجة نفس الطلب عدة مرات لها نفس تأثير معالجته مرة واحدة. على سبيل المثال، إذا وصلك Webhook بحدث order.created مع رقم الطلب 123، يجب أن تتحقق أولاً في قاعدة بياناتك “هل الطلب رقم 123 موجود بالفعل؟” قبل إنشائه. هذا يمنع إنشاء طلبات مكررة.
4. استخدم أدوات لتسهيل الاختبار
أثناء التطوير، يعمل خادمك على جهازك المحلي (localhost). هذا العنوان غير متاح على الإنترنت، فكيف ستصل إليه الـ Webhooks من GitHub أو Stripe؟
الحل: استخدم خدمات الأنفاق (Tunneling Services). أشهرها وأسهلها هو ngrok. بلمح البصر، يقوم ngrok بإنشاء عنوان URL عام آمن (https.) يوجه كل الطلبات الواردة إليه مباشرة إلى المنفذ الذي تحدده على جهازك المحلي. هذه الأداة لا تقدر بثمن لتطوير واختبار الـ Webhooks.
الخلاصة: متى نستخدم هذا أو ذاك؟
بعد كل هذا الشرح، القرار بسيط جداً:
- استخدم Webhooks (الخيار الأفضل في 90% من الحالات): عندما تحتاج إلى تحديثات فورية، وعندما تتعامل مع واجهة برمجية تدعمها، وعندما تهتم بكفاءة الموارد وقابلية التوسع. إنها الطريقة الحديثة والفعالة لبناء أنظمة متجاوبة.
- استخدم Polling (في حالات نادرة ومحددة): عندما لا تدعم الواجهة البرمجية للطرف الآخر الـ Webhooks (وهذا أصبح نادراً)، أو عندما لا تحتاج إطلاقاً لتحديثات فورية والتأخير لدقائق أو ساعات مقبول، أو في التطبيقات البسيطة جداً التي لا يمثل فيها استهلاك الموارد مشكلة.
في قصتنا، بمجرد أن انتقلنا إلى استخدام الـ Webhooks، انخفض الحمل على خوادمنا بنسبة تزيد عن 95%. أصبحت تحديثات المخزون فورية، واختفت شكاوى البطء، وعادت الحياة إلى طبيعتها. لقد كانت نقلة نوعية حقيقية.
نصيحتي الأخيرة لكم: توقفوا عن جعل خوادمكم تستجدي المعلومات. انتقلوا من الاستقصاء إلى الاستماع، فخوادمكم ستشكركم على ذلك! 😉