لماذا المعالجة غير المتزامنة؟ الفصل بالقوائم
لماذا المعالجة غير المتزامنة؟ الفصل بالقوائم
تعتمد تقريباً كل منظومة كبيرة استخدمتها على الإطلاق — Gmail وUber وGitHub وStripe — على المعالجة غير المتزامنة في جوهرها. ومع ذلك، يبدأ معظم المطورين مسيرتهم بكتابة كود متزامن بالكامل: يُرسل المُستدعِي طلباً، ينتظر النتيجة، ثم يكمل. هذا النموذج بسيط ويمكن التنبؤ بسلوكه، لكنه يتعطل عند التوسع بطرق تستحق الفهم العميق قبل اللجوء إلى القوائم كعلاج.
النموذج المتزامن ومواطن فشله
في الاستدعاء المتزامن، يظل المُستدعِي محجوباً حتى يستجيب المُستدعَى. تأمل مستخدماً يسجل حساباً جديداً على منصة تجارة إلكترونية. قد تُنفِّذ تطبيقاً متزامناً ساذجاً الخطوات التالية:
- إدراج سجل المستخدم في قاعدة البيانات (~5 مللي ثانية)
- إرسال بريد ترحيبي عبر خادم SMTP (~300–800 مللي ثانية)
- تشغيل فحص احتيال عبر استدعاء واجهة برمجة تطبيقات خارجية (~200–500 مللي ثانية)
- إنشاء كائن عميل Stripe (~150–400 مللي ثانية)
- نشر إشعار Slack في قناة
#new-signups(~100–300 مللي ثانية) - إعادة استجابة 201 للمتصفح
يمكن أن تُضيف الخطوات 2–5 بسهولة 750 مللي ثانية إلى ثانيتين من الكمون لطلب يراه المستخدم مجرد "إنشاء حسابي". والأسوأ، إذا انتهت مهلة خادم SMTP في الخطوة 2، يحصل المستخدم على خطأ — رغم أن الحساب قد أُنشئ بالفعل. وقت استجابة الطلب بأكمله أصبح رهينة للنظام الأبطأ أو الأقل موثوقية في المراحل اللاحقة.
هذه هي أنماط الفشل الجوهرية في الاقتران المتزامن:
- تضخيم الكمون — وقت استجابة المُستدعِي يساوي مجموع جميع الاستدعاءات اللاحقة، لا أسرعها.
- الأعطال المتسلسلة — نظام لاحق بطيء يُبقي الخيوط مشغولة، ما يُنهك مجموعات الخيوط ويتسبب في انتهاء مهل الطبقات الأمامية.
- الاقتران الزمني — يجب أن تعمل كلتا الخدمتين في الوقت ذاته؛ النشر التدريجي لخدمة البريد يُعطل التسجيلات.
- اقتران الحِمل — ارتفاع مفاجئ في حركة نقطة التسجيل يُفضي فوراً إلى ارتفاع مفاجئ في الحِمل على خادم البريد وStripe وواجهة برمجة الكشف عن الاحتيال في آن واحد.
القائمة بوصفها حاجزاً فاصلاً
قائمة انتظار الرسائل هي حاجز مُدار ومُرتَّب يجلس بين المُنتِج (الخدمة التي تُنشئ العمل) وواحد أو أكثر من المُستهلِكين (الخدمات التي تُنجزه). يكتب المُنتِج رسالة ويعود فوراً. يقرأ المُستهلِك الرسالة ويعالجها بشكل مستقل، وفق وتيرته الخاصة.
هذا الإدراج البسيط لحاجز يُزيل كل مشاكل الاقتران المذكورة آنفاً:
- الكمون: يكتب خادم API سجلاً واحداً في قاعدة البيانات ويضع رسالة JSON صغيرة في القائمة. تكتمل كلتا العمليتين في أقل من 10 مللي ثانية. يحصل المستخدم على استجابة 201 فوراً.
- عزل الأعطال: إذا توقفت خدمة البريد 20 دقيقة، تتراكم الرسائل في القائمة. حين تستعيد عملها، تُفرغ الرصيد المتراكم. لم يعد نقطة التسجيل تُعيد أي خطأ.
- تسوية الحِمل: ارتفاع مفاجئ في الحركة يُضيف 50,000 رسالة دفعةً واحدة. يعالج عمال البريد الرسائل بمعدل ثابت — لنقل 500/ثانية — بمعزل عن الارتفاع المفاجئ. لا يتسرب أي ارتفاع إلى الأنظمة اللاحقة.
- التوسع المستقل: يمكنك توسيع خوادم API وعمال البريد بشكل مستقل تماماً. أضف 10 خوادم API دون المساس بأسطول عمال البريد، والعكس صحيح.
- النشر المستقل: انشر عمال البريد بشكل تدريجي بينما التسجيلات نشطة. الرسائل الجديدة تنتظر في القائمة؛ القديمة تُعالَج بالإصدار الذي لا يزال يعمل.
المتزامن مقابل غير المتزامن — متى تختار كلاً منهما
المعالجة غير المتزامنة ليست أفضل في كل الأحوال. يعتمد الاختيار على ما إذا كان المُستدعِي يحتاج إلى نتيجة العملية اللاحقة لكي يكمل.
- يجب أن يكون متزامناً: تفويض الدفع (لا يمكنك إخبار المستخدم "تم قبول الدفع" قبل تأكيد البنك)، حجز المخزون (تحتاج معرفة توفر المنتج)، التحقق من الهوية (لا يمكنك إصدار رمز قبل التحقق من بيانات الاعتماد).
- يجب أن يكون غير متزامن: إرسال الإشعارات (بريد، رسائل، إشعارات دفع)، توليد التقارير أو ملفات PDF، تغيير حجم الصور المرفوعة، تحديث فهارس البحث، كتابة سجلات المراجعة، تحصيل الاشتراكات المتكررة، مزامنة البيانات مع مسارات التحليلات.
اختبار ذهني مفيد: "إذا فشلت هذه الخطوة بصمت الآن، هل سيكون الإجراء الجوهري للمستخدم خاطئاً أو غير صالح؟" إذا كانت الإجابة نعم، أبقِها متزامنة. إذا كانت لا — إذا كان الفشل قابلاً للتعافي لاحقاً — اجعلها غير متزامنة.
الأرقام الحقيقية التي تُجسِّد الأمر
تأمل مسار CI الخاص بـGitHub. حين تدفع commit، يجب على GitHub:
- قبول الدفع، تحديث المرجع، والإقرار لعميل git — يجب أن يكون متزامناً (~40 مللي ثانية).
- تشغيل إنشاءات CI عبر عشرات سير العمل المحتملة — غير متزامن.
- إرسال إشعارات بريد/Slack للمتابعين — غير متزامن.
- تحديث حالة النشر لدى مزود السحابة — غير متزامن.
- إعادة فهرسة الـcommit للبحث في الكود — غير متزامن.
لو كانت جميعها متزامنة، كان git push سيُعلِّق المستخدم 10–30 ثانية. يعالج GitHub أكثر من 2 مليار حدث يومياً عبر مسار غير متزامن. فعل ذلك بشكل متزامن مستحيل جسدياً — قاعدة بيانات واحدة لا تستوعب 23,000 كتابة في الثانية مع نشر متزامن لعشرة أنظمة لاحقة.
ما الذي تكلفك إياه فكرة الفصل
المعالجة غير المتزامنة ليست مجانية. فهم المقايضات هو ما يميز التصميم المدروس عن الذي يتسبب في كوارث تشغيلية:
- الاتساق التدريجي: يصل البريد الترحيبي للمستخدم بعد ثوانٍ (أو دقائق) من التسجيل، لا بعد مللي ثوانٍ. في معظم الحالات هذا مقبول؛ في بعضها لا يكون كذلك.
- تعقيد الملاحظة: استدعاء متزامن له سطر سجل واحد وامتداد تتبع واحد. مسار غير متزامن يمتد عبر خدمات متعددة وقراءات قوائم وإعادة محاولات — تحتاج تتبعاً موزعاً لمتابعة مهمة من البداية للنهاية.
- رؤية الأعطال: إذا تعطل عامل بصمت وتوقف عن المعالجة، تتراكم الرسائل بشكل غير مرئي ما لم تراقب عمق القائمة وتأخر المستهلك.
- ضمانات الترتيب: كثيراً ما تُسلِّم القوائم الرسائل بترتيب مختلف عند حدوث أعطال. إذا كان المستهلكون يتطلبون ترتيباً صارماً (مثل: إنشاء الحساب ← التحقق منه ← تحصيل رسومه)، فيجب تصميم ذلك صراحةً.
- التسليم المكرر: معظم القوائم تضمن التسليم مرةً على الأقل، ما يعني احتمال تسليم الرسالة ذاتها أكثر من مرة. يجب أن يكون المستهلكون متكافئي الأثر — معالجة الرسالة ذاتها مرتين يجب أن تُنتج النتيجة ذاتها كمعالجتها مرة واحدة. (هذا موضوع الدرس 6 بالتفصيل.)
بناء الحدس: تشبيه مكتب البريد
أسهل طريقة لاستيعاب هذا النموذج هي من خلال تشبيه مادي. التواصل المتزامن يشبه مكالمة هاتفية: يجب أن يكون الطرفان متاحَين في الوقت ذاته، يظل المُتصل محجوباً حتى يرد الطرف الآخر، وإذا كان الخط مشغولاً يفشل الاتصال. أما التواصل غير المتزامن فيشبه البريد العادي: تكتب رسالة، تضعها في قائمة انتظار (صندوق البريد)، وتكمل يومك. تُوصلها الخدمة البريدية متى استطاعت. يقرأ المستقبل الرسالة ويرد وفق وتيرته. لا يحتاج أي من الطرفين التوفر في الوقت ذاته. النظام يتسامح مع التأخيرات وإعادة المحاولات وحتى الأعطال المؤقتة.
قوائم الانتظار في الأنظمة الموزعة تعمل بالضبط بهذه الطريقة. المُنتِج لا يعرف ولا يهتم بعدد المستهلكين الذين سيعالجون رسالته، ولا بالمدة التي سيستغرقونها، ولا بما إذا كانوا يعملون حالياً. هذا الاستقلال هو ما يجعل الأنظمة الكبيرة صامدة.