انتقل للمحتوى
علي هيثم·TECH·يردّ خلال أقل من ساعة
  • الرئيسيةابدأ من هنا
  • الأعمالدراسات حالة، مشاريع
  • الخدماتما الذي سأبنيه لك
  • المدونةمقالات، سلاسل
  • التدريبدورات، مختبرات، دورات
  • عنّيالمهندس وراء هذا الموقع
تسجيل الدخولابدأ مشروع
علي هيثم · TECH
  • الرئيسية↗
  • الأعمال↗
  • الخدمات↗
  • المدونة↗
  • التدريب↗
  • عنّي↗
تسجيل الدخولابدأ مشروع
متاح
علي هيثم·TECH

استوديو هندسي. دمشق، GMT+3.

aliyosef.online

الاستوديو

  • الأعمال
  • المدونة
  • التدريب
  • عنّي
  • تواصل معي

البوابة

  • تسجيل الدخول
  • فتح تذكرة دعم
  • متابعة مشروع
  • حسابي

مصادر

  • الوثائق
  • حالة الأنظمة
  • سجلّ التغييرات
  • حزمة العلامة
  • الخصوصية
  • الشروط

النشرة

ملاحظات تقنية وأخبار، أسبوعياً. بدون حشو.

مجاني. إلغاء الاشتراك متى ما حبيت.

تابعني على
© 2026 علي هيثم يوسف. جميع الحقوق محفوظة.مبنيّ يدويًا بـ React 19.آخر نشر · 2026-05-08كل الأنظمة تعمل
دورةمحرّكات المزامنة، من طرف لطرف· في الدرس ٠١.٠٢↗
  1. الرئيسية/
  2. المدوّنة/
  3. محرّكات المزامنة
محرّكات المزامنة

متى الـ polling أفضل من الـ websockets

سنة من الـ trade-offs بمحاسب. ليش الجواب الواضح طلع غلط على ظروف الشبكة عندنا.

٥ أيار ٢٠٢٦·8 دقائق·بقلم علي يوسف

جاري تحميل المقال…

شاركXLinkedInRSS
استلم المقال التالي في بريدك.مقال جديد كل أحد. هندسة بلغة واضحة. مجاني، يمكنك إلغاء الاشتراك في أي وقت.اشترك→
AY
علي هيثم يوسف

مهندس وكاتب. أبني منصات متعدّدة المستأجرين، محرّكات مزامنة، وأنظمة موثوقة بهدوء. أكثر من ٦٠٠ مشروع مُسلَّم منذ ٢٠١٥.

اشتركابدأ مشروعاً→

اقرأ بعدها

  • CR
    ٢٨ نيسان ٢٠٢٦·محرّكات المزامنة

    حلّ التعارضات في تطبيقات offline-first

    Last-write-wins غالباً غلط. هاد اللي بيشتغل لما اثنين clients يختلفوا.

    10 دقائق
  • SE
    Series
    ٢٠ نيسان ٢٠٢٦·محرّكات المزامنة

    بناء محرّك مزامنة، الجزء الأول: الـ queue

    افتتاح السلسلة. شكل الـ queue الصادر، ليش الترتيب مهم، والـ test harness اللي بيمسك كل شي.

    14 دقيقة
  • RS
    ١٢ نيسان ٢٠٢٦·محرّكات المزامنة

    مشكلة الـ reconnect storm

    لما 800 موبايل يرجعوا متّصلين بنفس الوقت، شو بصير للسيرفر. الأنماط اللي بتصمد.

    7 دقائق
←العودة إلى كل المقالات

طوال عام ٢٠٢٤ كنّا متأكّدين أنّنا بحاجة إلى websockets. كان المنتج منصّة محاسبة متعدّدة المستأجرين، مع تطبيق Flutter على العميل وخلفيّة Laravel على الخادم؛ والبيانات يجب أن تبدو حيّة عبر الأجهزة، والإجابة المعتادة في الكتب هي «افتح websocket وادفع البيانات». هذا ما فعلناه. بعد ستّة أشهر، أزلناه واستبدلناه بـ polling منضبط، وتحسّن زمن الاستجابة من منظور المستخدم. هنا ما تعلّمناه، ومتى يفوز polling فعلاً.

الإجابة المعتادة صحيحة، نظريّاً#

الحجّة المؤيّدة لـ websockets معروفة، وليست خاطئة:

  • اتّصال واحد دائم، بلا overhead في كلّ طلب.
  • الخادم يدفع البيانات لحظة حدوث التغيير.
  • بايتات أقلّ تراكميّاً عبر الجلسة الطويلة.

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

ما الذي تغيّر حين خرجنا من المختبر#

تطبيق المحاسبة يُستخدم في محلات ومستودعات ومطاعم ومكاتب في الريف السوري والسعودي والخليج. ظروف الشبكة هناك ليست ظروف لابتوبك في مقهى. تحديداً:

  1. معظم جلسات الجوّال ليست دائمة. الأجهزة تنتقل بين الأبراج كلّ بضع دقائق؛ والـ NAT على مستوى المشغّل صارم؛ وبعض الشبكات تقطع اتّصالات TCP الخاملة بعد ستّين ثانية.
  2. نقاط الـ WiFi تعيد الإقلاع. الراوترات تعيد الإقلاع. الهواتف تنام والشاشة مغلقة والراديو بارد. منطق إعادة الاتّصال يجب أن يكون مثاليّاً وإلّا لاحظ المستخدم أن شيئاً لم يصل.
  3. كثير من الأجهزة قديم. هاتف Android 9 بـ 2 GB من الذاكرة وبراوزر webview لا يحتمل websocket worker مع باقي runtime تطبيقك. ضغط الذاكرة يقتل الاتّصال بصمت.
الملاحظة الصريحة

الـ websockets تفترض أن الشبكة لها ذاكرة. على شبكات المستهلكين الحقيقيّة، الشبكة مصابة بالنسيان.

عاصفة إعادة الاتّصال#

نقطة الانهيار كانت يوم ثلاثاء صباحاً. كان التيّار مقطوعاً طوال الليل في مدينة لدينا فيها عدّة مئات من المستخدمين. حين عاد التيّار، عادت كلّ هذه الهواتف إلى الإنترنت في نفس الدقيقة. حاولت كلّها إعادة فتح websockets في نفس الدقيقة. خادم Reverb سقط. حصل المستخدمون على شاشة فارغة. وحصلنا نحن على مكالمة.

يمكنك هندسة الحلّ ضدّ العاصفة. backoff عشوائي، حدود معدّل على الخادم، connection pool، gateway منفصل. فعلنا كلّ ذلك. ساعد. لكنّه لم يبدُ موثوقاً.

لماذا بدأ polling يفوز#

لـ polling خاصيّة يسهل تجاهلها لأنّنا تعلّمنا أن نراه بدائيّاً: كلّ طلب مستقلّ. لا حالة مشتركة على السلك. لا سؤال «هل الاتّصال لا يزال سليماً؟» لتجيب عنه. العميل يسأل؛ الخادم يجيب؛ ننتقل.

هذا الشكل متسامح بطرق ليست في websockets:

  • محاولة polling فاشلة تعيد المحاولة في الفاصل التالي. لا حالة «أنبوب مكسور» لاستردادها.
  • إعادة تشغيل الخادم تُسقِط صفر اتّصالات، لأنّه لا يوجد اتّصالات لإسقاطها.
  • تخزين HTTP المؤقّت، حواف CDN، الطلبات المشروطة بـ If-Modified-Since كلّها تصبح متاحة من جديد. websockets تتنازل عن كلّ هذه البنية.
  • المراقبة بسيطة. كلّ poll هو سطر HTTP عاديّ في السجلّ. لا أدوات خاصّة.

ما الذي يعنيه «polling منضبط» فعلاً#

لم نغيّر الرابط فقط. طبقة polling التي حلّت محلّ websockets لها خصائص استلزمت تكرارات لضبطها:

interface PollConfig {
  intervalActiveMs: 8_000;     // المستخدم يتفاعل
  intervalIdleMs:   45_000;    // التبويب ظاهر، لا إدخال حديثاً
  intervalHiddenMs: 0;         // التبويب مخفيّ، لا تفعل شيئاً
  jitterRatio:      0.2;       // ±٢٠٪ كي لا يتزامن أسطول الأجهزة
  maxConcurrent:    1;         // لا تتداخل عمليّات polling
  cursor:           'updated_at';
}

أمور تستحقّ إخراجها:

واعٍ للظهور#

حين يكون التبويب مخفيّاً، لا نقوم بالـ polling. حين يصبح ظاهراً، نقوم بـ poll واحد فوراً، ثمّ نستأنف الفاصل. هذا التغيير الواحد قلّص حركة الباك-إند لدينا بنسبة ٦٠٪، وحسّن زمن الاستجابة المُدرَك، لأنّ التبويب الأمامي يقوم بـ poll خلال ميلّي ثوانٍ من الظهور.

مبنيّ على cursor، لا على fetch كامل#

كلّ poll يرسل أعلى updated_at رآه، والخادم يعيد فقط الصفوف الأحدث منه. الردّ شبه دائماً صغير. معظم عمليّات polling تعيد صفر صفّ.

واحد في كلّ مرّة#

نطوي عمليّات polling المتداخلة في واحدة. إن كان هناك poll قيد التنفيذ حين يحلّ الفاصل التالي، نتجاوز الفاصل وندَع الجاري ينتهي. هذا يبدو بديهيّاً؛ وهو مصدر نصف الأخطاء التي ستراها في حلقات polling مكتوبة على عجل.

بعد أسبوعين من polling المنضبط، صار متوسّط زمن الاستجابة المُدرَك لـ«المعاملة تظهر على الجهاز الثاني» ٤٫٦ ثانية. مع websockets كان ١١ ثانية، حين كان يعمل. البروتوكول الأسوأ على الورق فاز في المقياس الذي يهمّ.

سجلّ هندسي، الأسبوع الثالث والعشرون

متى لا تزال تريد websockets فعلاً#

polling ليس الجواب لكلّ شيء. الحالات التي أبقينا فيها push:

  • الإشعارات. أحداث منفردة، نادرة، عالية الأهميّة، يكون فيها تأخير حتّى عشر ثوانٍ سيّئاً. نستخدم قناة Reverb منفصلة بحمولة رقيقة، ونعامل الـ websocket على أنّه best-effort: إن سقط، يلاحظ polling نفس الصفّ في الوقت المناسب.
  • اللوحات الحيّة. لوحات العمليّات حيث يحدّق المُشغِّل في الشاشة لساعات. الجلسة طويلة، الحمل صغير، والاتّصال الدائم يعوّض نفسه.
  • التحرير المتزامن. حين يكتب شخصان في نفس النموذج، تحتاج push. polling لا يستطيع خدمة موضع cursor كلّ ثلاثين ميلّي ثانية.

خارج هذه الحالات، الافتراض هو polling.

الخطّ الذي نرسمه الآن#

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

إن انكسر أيّ من هذه الشروط، فإنّ polling، حين يُنفَّذ بانضباط، هو الإجابة التي تنجو من الاحتكاك بالعالم الحقيقي.

هذا ليس الاستنتاج المعماري الذي يريده الكتاب. إنّه الاستنتاج الذي يصمد في الثامنة صباح ثلاثاء بعد انقطاع التيّار.


المقال التالي في هذا الخطّ يدور حول كيفيّة هيكلة الـ queue الصادر على العميل ليبقى polling-based sync صحيحاً تحت تذبذب الشبكة. إن كانت محرّكات المزامنة تهمّك، صفحة السلسلة تستكمل من حيث ينتهي هذا المقال.