اختبار الأداء والتحميل

مفاهيم اختبار الحمل

18 دقيقة الدرس 2 من 28

مفاهيم اختبار الحمل

قبل أن تكتب سكريبت k6 واحداً أو تشغّل مجموعة JMeter، تحتاج إلى مفردات مشتركة. الفرق بالتعريف الدقيق بين اختبار الحمل واختبار الضغط يفصل بين فريق يستخلص استنتاجات صحيحة من بياناته وآخر يعلن صحة خدمته بينما هي على بعد موجة مرورية واحدة من استدعاء مهندس الدوام الليلي. هذا الدرس يرسي التعريفات الدقيقة والنموذج الذهني الذي ستحمله في كل قرار تصميم اختبار.

أنواع الاختبارات الأربعة

كل نوع يجيب على سؤال هندسي مختلف. استخدام النوع الخطأ مكلف بقدر تخطي الاختبار كلياً، لأنك تنال ثقة في الخاصية الخطأ.

اختبار الحمل (Load Test)

اختبار الحمل يتحقق من أن النظام يحقق أهداف SLO عند مستوى حركة المرور الإنتاجي المتوقع — ويُعرَّف عادةً بمستوى الذروة أو P95 للمعدل المرصود. السؤال هو: هل يؤدي النظام وظيفته بشكل صحيح تحت الحمل الاعتيادي؟ المدة عادة 10–30 دقيقة؛ كافية ليستقر الـ JVM واشباع حوض الاتصالات ودورات GC، لكن ليست طويلة لدرجة قياس الانجراف.

في شركات بحجم Google وMeta، يُشتق "الحمل المتوقع" من خطط السعة وتوقعات المرور، لا من التخمين. تستخرج P95 QPS من لوحات Prometheus (تعرف كيف تفعل ذلك) وتعيد إنتاج ذلك المعدل بتوزيعات طلبات واقعية.

اختبار الضغط (Stress Test)

اختبار الضغط يدفع المرور إلى ما بعد الذروة المتوقعة للعثور على نقطة الانهيار — مستوى الحمل الذي يتراجع فيه الزمن الاستجابي عن SLO، أو ترتفع معدلات الأخطاء، أو تتعطل العملية. السؤال: ما السقف الأقصى لسعة النظام، وكيف يفشل؟

السؤال المهم بالقدر ذاته هو ما يحدث بعد نقطة الانهيار: هل يتعافى النظام ذاتياً حين ينخفض الحمل، أم يحتاج إعادة تشغيل يدوية؟ يرفع اختبار الضغط الجيد الحمل بخطوات (مثلاً 100% → 150% → 200% من الحمل المتوقع)، يتوقف عند كل خطوة في نافذة الحالة الثابتة، ثم ينزل مجدداً.

اختبار النقاهة (Soak Test)

اختبار النقاهة (يُسمى أيضاً اختبار التحمل) يشغّل النظام عند الحمل الاعتيادي أو أعلى قليلاً لفترة ممتدة — ساعات إلى أيام. السؤال: هل يتراجع الأداء مع الوقت؟ هذا الاختبار هو ما يكشف تسريبات الذاكرة، استنفاد حوض الاتصالات، امتلاء قرص الـ log، تجزؤ الـ cache، وانتفاخ قاعدة البيانات التي لا تظهر إلا بعد آلاف الطلبات.

اختبارات النقاهة هي الأكثر تجاهلاً في خطوط CI، ومع ذلك تكتشف الفئة الأكثر احتمالاً لإحداث حوادث الثالثة صباحاً في الأسبوع الثاني من الإطلاق. إن كان بإمكانك إجراء اختبار طويل واحد فقط قبل إصدار رئيسي، أجرِ اختبار نقاهة بـ 80% من الذروة المتوقعة لمدة 4–8 ساعات.

اختبار الارتفاع المفاجئ (Spike Test)

اختبار الارتفاع المفاجئ يعرّض النظام لزيادة مفاجئة حادة في الحمل — من الخمول أو الخط الأساسي إلى أضعاف مضاعفة من الذروة — ثم ينخفض بنفس السرعة. السؤال: كيف يستجيب النظام للانفجارات المفاجئة في المرور؟ هذا النوع يحاكي مباشرة السيناريوهات الواقعية: منتج ينتشر بشكل فيروسي، تخفيضات فلاش، تغريدة مشهور، أو مهمة cron تطلق آلاف العمال المتوازيين في منتصف الليل.

يكشف اختبار الارتفاع المفاجئ المُصمَّم جيداً ما إذا كان autoscaling يتفاعل بسرعة كافية، وما إذا كانت أحواض الاتصالات تُصفِّف أم ترفض تحت الارتفاع، وما إذا كانت circuit breakers تعمل قبل أن تتسلسل الخدمات الداخلية في الفشل.

Four load test profiles — traffic shape over time Load Test Profiles — Traffic Shape Over Time Load Test Steady at expected peak Stress Test break Ramp to breaking point Soak Test Hours at normal load, watch drift Spike Test Sudden burst then drop → time → time → time → time
الأنماط الأربعة لاختبارات الحمل وأشكال المرور المميزة لكل منها.

النموذجان: المفتوح والمغلق

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

النموذج المغلق (Closed Workload Model)

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

هذا يحاكي نموذج حوض الاتصالات أو الخادم بخيط لكل طلب: الحوض لديه N خيط؛ حين تكون كلها مشغولة، تصطف الطلبات الجديدة. معظم أدوات اختبار الحمل تعتمد النموذج المغلق افتراضياً (خيوط JMeter، مستخدمو Gatling، VUs في k6 في حلقة الافتراضية).

الخاصية الجوهرية للنموذج المغلق: حين يتباطأ نظامك، يتباطأ معدل الوصول أيضاً. هذا يعني أن النموذج المغلق قادر على إخفاء الإفراط في الحمل. يبدو أن النظام يتعامل مع 500 VU بشكل جيد — لكن فقط لأنه يعالج 50 RPS حين يجب أن يعالج 500 RPS. في الإنتاج، العملاء الحقيقيون يستمرون في الوصول بوتيرتهم الخاصة بصرف النظر عن بطء خدمتك.

النموذج المفتوح (Open Workload Model)

في النموذج المفتوح، تصل الطلبات بمعدل ثابت (مثلاً 1000 RPS)، بغض النظر عن مدة معالجة النظام لها. إن ارتفع الزمن الاستجابي، تتراكم الطلبات في قائمة. تكبر القائمة حتى تفيض (مهلة العميل أو استنفاد الذاكرة) — وهذا بالضبط ما يحدث مع حركة HTTP الحقيقية، أو مستهلكي تدفق الأحداث، أو منتجي طوابير الرسائل.

النماذج المفتوحة أصعب تنفيذاً لأن مولّد الحمل يجب أن يحافظ على معدل وصول مستهدف حتى عندما تكون الردود بطيئة، وهذا يتطلب طاقة أكبر بكثير من المولّد. في k6 يُنفَّذ ذلك بمشغّل constant-arrival-rate، وفي JMeter بإضافة Throughput Shaping Timer.

القاعدة العملية الإنتاجية: مثّل حركة مرور الخارج (المواجهة للإنترنت) بنموذج مفتوح، ومثّل استدعاءات الخدمات الداخلية المقيّدة بحوض خيوط بنموذج مغلق. خدمة الدفع التي تخدم متصفحات المستخدمين هي نموذج مفتوح؛ حوض عمال يستهلك من Kafka هو نموذج مغلق.
Open vs Closed workload model comparison Open vs Closed Workload Models Closed Model VU Pool (fixed N) request Service VU waits, then retries Throughput ∝ N / mean latency ⚠ Slower service → fewer RPS → masks overload Good for: connection pools, thread pools Default in: JMeter, Gatling, k6 basic VU loop Open Model Arrival Rate (fixed RPS) Queue grows! Svc Arrival rate independent of latency ✓ Queue grows → accurately models overload Good for: public HTTP, event streams In k6: constant-arrival-rate executor
النموذج المغلق — الإنتاجية تنخفض مع الزمن الاستجابي؛ النموذج المفتوح — معدل الوصول ثابت وتكبر القائمة بشكل مستقل.

إعداد k6 لكل نوع

فهم النظرية شيء، ومعرفة أي مشغّل k6 تستخدم بالضبط هو ما تحتاجه فعلياً. فيما يلي نقاط بداية جاهزة للإنتاج لكل نوع. تمنحك واجهة scenarios (المُدخَلة في k6 v0.27) تحكماً دقيقاً في نوع المشغّل وشكل الارتفاع وخيارات كل سيناريو.

// إعداد سيناريوهات k6 — الأنواع الأربعة (قسم options فقط) // الملف: k6-profiles.js export const options = { scenarios: { // --- 1. اختبار الحمل (نموذج مغلق، VUs ثابتة) --- load_test: { executor: 'ramping-vus', startVUs: 0, stages: [ { duration: '2m', target: 200 }, // صعود تدريجي { duration: '15m', target: 200 }, // حالة ثابتة عند الذروة المتوقعة { duration: '2m', target: 0 }, // هبوط تدريجي ], gracefulRampDown: '30s', }, // --- 2. اختبار الضغط (نموذج مفتوح، معدل وصول متدرج) --- stress_test: { executor: 'ramping-arrival-rate', startRate: 100, timeUnit: '1s', preAllocatedVUs: 500, maxVUs: 2000, stages: [ { duration: '5m', target: 100 }, // 100 RPS — الخط الأساسي { duration: '5m', target: 250 }, // 250 RPS { duration: '5m', target: 500 }, // 500 RPS — الذروة المتوقعة { duration: '5m', target: 750 }, // 150% — ضغط { duration: '5m', target: 1000 }, // 200% — البحث عن الانهيار { duration: '5m', target: 100 }, // التعافي ], }, // --- 3. اختبار النقاهة (نموذج مغلق، حمل مستمر) --- soak_test: { executor: 'constant-vus', vus: 150, // ~80% من تزامن الذروة المتوقعة duration: '4h', }, // --- 4. اختبار الارتفاع المفاجئ (نموذج مفتوح، وصول انفجاري) --- spike_test: { executor: 'ramping-arrival-rate', startRate: 50, timeUnit: '1s', preAllocatedVUs: 1000, maxVUs: 3000, stages: [ { duration: '1m', target: 50 }, // خط أساسي خامل { duration: '30s', target: 2000 }, // ارتفاع: 40x خلال 30 ثانية { duration: '2m', target: 2000 }, // الإبقاء على الارتفاع { duration: '30s', target: 50 }, // الهبوط { duration: '5m', target: 50 }, // مراقبة التعافي ], }, }, thresholds: { http_req_duration: ['p(95)<500', 'p(99)<1500'], http_req_failed: ['rate<0.01'], }, };
لا تشغّل الأنواع الأربعة في وقت واحد — ستُشبع مولّد الحمل قبل أن تُشبع الهدف. أبقِ سيناريو واحداً نشطاً لكل تشغيل، واستخدم export options منفصلاً لكل ملف أو العَلَم --config لاختيار القسم الصحيح. في CI، شغّل اختبار الحمل في كل PR؛ اقصر اختبارات الضغط والنقاهة على الخطوط الليلية أو مرحلة ما قبل الإصدار.

اختيار النوع المناسب للسؤال الصحيح

المهندس المتمرس يختار النوع من السؤال الهندسي لا من العادة:

  • التحقق من SLO قبل نشر: اختبار حمل عند P95 QPS المتوقعة.
  • تخطيط السعة لنمو 3x: اختبار ضغط من 1x إلى 4x.
  • التحقيق في تسريب ذاكرة بعد حادثة الأسبوع الثاني: اختبار نقاهة لـ 6–8 ساعات.
  • التحقق من autoscaler وcircuit-breaker: اختبار ارتفاع مفاجئ بـ 10–20x.
  • تجربة فوضى تحت حركة مرور حقيقية: شغّل اختبار نقاهة كخلفية مع حقن أعطال.

في الدرس التالي ستطبّق هذا عملياً بسكريبتات k6 كاملة، وعتبات مرتبطة بـ SLOs حقيقية، ودمج في خط CI يمنع النشر عند انحدار الأداء.