أحد أسئلتي المفضلة في المقابلات هو "ماذا تخبرك كلمات مثل async و await ؟" لأنها تفتح لك الفرصة لإجراء مناقشة شيقة مع الشخص الذي تجري معه المقابلة... أو لا تفتحها لأنها تدور حول هذا الموضوع. في رأيي، من المهم للغاية أن نفهم سبب استخدامنا لهذه التقنية.
أشعر أن العديد من المطورين يفضلون الاعتماد على عبارة "إنها أفضل ممارسة" واستخدام الأساليب غير المتزامنة بشكل أعمى.
توضح هذه المقالة الفرق بين الطرق المتزامنة وغير المتزامنة في الممارسة العملية.
سأجري اختبارًا بالطريقة التالية. يتم تشغيل نسختين مستقلتين من Locust على جهازين. تحاكي نسخ Locust مستخدمًا يقوم بما يلي:
تحت الغطاء، يتصل كل تطبيق من تطبيقات الخدمة بقاعدة بياناته الخاصة وينفذ استعلام SELECT يستغرق خمس ثوانٍ ويعيد بضعة صفوف من البيانات. راجع كود وحدة التحكم أدناه للحصول على مراجع. سأستخدم Dapper لإجراء مكالمة إلى قاعدة البيانات. أود أن ألفت انتباهك إلى حقيقة أن نقطة النهاية غير المتزامنة تستدعي قاعدة البيانات بشكل غير متزامن أيضًا ( QueryAsync<T> ).
من الجدير بالذكر أنني أقوم بنشر نفس الكود لكلا خدمتي التطبيق.
أثناء الاختبار، ينمو عدد المستخدمين بالتساوي حتى يصل إلى العدد المستهدف ( عدد المستخدمين ). يتم التحكم في سرعة النمو من خلال معلمة معدل الظهور (عدد المستخدمين الفريدين الذين يتم الانضمام إليهم في الثانية) - فكلما زاد العدد، زادت سرعة إضافة المستخدمين. يتم ضبط معدل الظهور على 10 مستخدمين/ثانية لجميع التجارب.
تقتصر جميع التجارب على 15 دقيقة.
يمكنك العثور على تفاصيل تكوين الجهاز في قسم التفاصيل الفنية للمقالة.
تشير الخطوط الحمراء إلى نقطة النهاية غير المتزامنة، والخطوط الزرقاء إلى نقطة النهاية المتزامنة، على التوالي.
هذا كل ما يتعلق بالنظرية، فلنبدأ.
يمكننا أن نرى أن كلا النقطتين النهائيتين تعملان بشكل مشابه - التعامل مع حوالي 750 طلبًا في الدقيقة مع متوسط وقت استجابة يبلغ 5200 مللي ثانية.
الرسم البياني الأكثر إثارة للاهتمام في هذه التجربة هو اتجاه الخيوط. يمكنك رؤية أرقام أعلى بكثير لنقطة النهاية المتزامنة (الرسم البياني الأزرق) - أكثر من 100 خيط!
ولكن هذا متوقع، ويتوافق مع النظرية ــ عندما يأتي طلب، ويجري التطبيق مكالمة إلى قاعدة البيانات، يتم حظر الخيط لأنه يتعين عليه الانتظار حتى اكتمال رحلة الذهاب والإياب. وبالتالي، عندما يأتي طلب آخر، يتعين على التطبيق إنتاج خيط جديد للتعامل معه.
يُثبت الرسم البياني الأحمر — عدد مؤشرات الترابط في نقطة النهاية غير المتزامنة — سلوكًا مختلفًا. فعندما يأتي طلب ويقوم التطبيق بإجراء مكالمة إلى قاعدة البيانات، يعود المؤشر إلى مجموعة مؤشرات الترابط بدلاً من حظره. وبالتالي، عندما يأتي طلب آخر، يتم إعادة استخدام هذا المؤشر المجاني. وعلى الرغم من تزايد الطلبات الواردة، لا يتطلب التطبيق أي مؤشرات ترابط جديدة، لذا يظل عدد مؤشرات الترابط كما هو.
يجدر ذكر المقياس الثالث - متوسط وقت الاستجابة . أظهرت كلتا النقطتين النهائيتين نفس النتيجة - 5200 مللي ثانية. لذا، لا يوجد فرق من حيث الأداء.
الآن حان الوقت لرفع الرهانات.
لقد ضاعفنا الحمل. وتتعامل نقطة النهاية غير المتزامنة مع هذه المهمة بنجاح - حيث يتراوح معدل طلباتها في الدقيقة حول 1500. وفي النهاية وصل الأخ المتزامن إلى رقم مماثل وهو 1410. ولكن إذا نظرت إلى الرسم البياني أدناه، فسترى أن الأمر استغرق 10 دقائق!
السبب هو أن نقطة النهاية المتزامنة تتفاعل مع وصول مستخدم جديد من خلال إنشاء سلسلة أخرى، ولكن تتم إضافة المستخدمين إلى النظام (فقط للتذكير بأن معدل الظهور هو 10 مستخدمين/ثانية) بشكل أسرع مما يمكن لخادم الويب التكيف معه. ولهذا السبب قام بوضع العديد من الطلبات في قائمة الانتظار في البداية.
ومن غير المستغرب أن يظل مقياس عدد الخيوط عند مستوى 34 تقريبًا لنقطة النهاية غير المتزامنة، في حين ارتفع من 102 إلى 155 لنقطة النهاية المتزامنة. وانخفض متوسط وقت الاستجابة بشكل مماثل لمعدل الطلب في الدقيقة - كان وقت الاستجابة المتزامنة أعلى بكثير في بداية التجربة. إذا احتفظت بالاختبار لمدة 24 ساعة، فإن الأرقام المتوسطة ستصبح متساوية.
وتهدف التجربة الثالثة إلى إثبات الاتجاهات التي تم الكشف عنها خلال التجربة الثانية - حيث يمكننا أن نرى المزيد من التدهور في نقطة النهاية المتزامنة.
إن استخدام العمليات غير المتزامنة بدلاً من المتزامنة لا يؤدي إلى تحسين الأداء أو تجربة المستخدم بشكل مباشر. أولاً، يعزز الاستقرار والقدرة على التنبؤ تحت الضغط. بعبارة أخرى، يرفع عتبة التحميل حتى يتمكن النظام من معالجة المزيد قبل تدهوره.
لتحقيق أفضل نتيجة اختبار، كان يجب عليّ تشغيل الاختبارات من جهازين افتراضيين موجودين في نفس الشبكة حيث توجد خدمات التطبيق المستهدفة.
ومع ذلك، افترضت أن تأخر الشبكة سيؤثر على كلا التطبيقين بطريقة مماثلة إلى حد ما. وبالتالي، لا يمكن أن يعرض هذا الهدف الرئيسي للخطر - مقارنة كيفية تصرف الطرق المتزامنة وغير المتزامنة.
ما الذي قمت باختراقه لإجبار نقطة النهاية المتزامنة على الأداء بشكل غير متزامن تقريبًا ورسم الرسم البياني أدناه (ظروف التجربة هي نفسها الموجودة في التجربة الثالثة - 200 مستخدم)؟