يا جماعة الخير، خليني أحكيلكم قصة صارت معي قبل كم سنة، قصة علّمتني درس ما بنساه. كنت وقتها شغال على تطبيق صغير، فكرته بسيطة وحلوة، وكنت مبسوط عليه كثير. شهور وأنا أبرمج وأصمم فيه، وكل شي كان ماشي “زي الساعة”. التطبيق سريع، الواجهات بتفتح بسرعة، وكل ميزة بضيفها بتشتغل من أول مرة. كنت أفرّجيه لأصحابي وقرايبي، والكل يحكيلي “ما شاء الله يا أبو عمر، شغل نظيف!”.
وصل يوم الإطلاق. بعد شوية تسويق بسيط على صفحات التواصل، بلشوا الناس يدخلوا على التطبيق. أول عشر مستخدمين… الأمور تمام. عشرين… ثلاثين… الوضع لسا تحت السيطرة. لما وصل العدد لقرابة الـ 100 مستخدم متزامن، بلشت المصايب. التطبيق صار أبطأ من السلحفاة، الصفحات بتحمّل بعد دهر، وبعدها… “Internal Server Error”. بلشت الإشعارات توصلني على إيميلي زي المطر، وقاعدة البيانات “ضربت” ورفضت تستقبل أي اتصال جديد. حسيت الدنيا بتلف فيي، وشغلي اللي تعبت عليه شهور قاعد بنهار قدام عيوني في دقائق. هذا كان جحيم الأعطال اللي بحكي عنه.
بعد ليلة طويلة من محاولات الإصلاح اليائسة، اكتشفت إن المشكلة ما كانت “bug” أو خطأ برمجي بالمعنى التقليدي. الكود كان صح، لكنه ما كان مصمم ليتحمل هذا “الحمل” المفاجئ. এখান থেকে (من هنا) تعلمت الدرس القاسي: التطبيق اللي بيشتغل كويس لمستخدم واحد، مش بالضرورة يشتغل كويس لمئة أو ألف. ومن يومها، صار “اختبار الحمل” أو الـ Load Testing جزء مقدس من شغلي، ما بستغني عنه أبدًا. واليوم، بدي أشارككم خبرتي هاي، عشان ما تقعوا بنفس الحفرة اللي وقعت فيها.
ما هو “اختبار الحمل” (Load Testing) يا أبو عمر؟ وليش هو مش رفاهية؟
ببساطة شديدة، تخيل إنك بنيت جسر. قبل ما تفتحه للسيارات، بتجيب أوزان ثقيلة وبتحطها عليه عشان تتأكد إنه بيتحمل، صح؟ هاد هو بالضبط اختبار الحمل للبرمجيات. هو نوع من “اختبارات الأداء” (Performance Testing) اللي بنقوم فيه بمحاكاة عدد كبير من المستخدمين الافتراضيين (Virtual Users) وهم بيستخدموا تطبيقك بنفس الوقت.
الهدف مش إننا “نكسر” التطبيق (هذا اسمه اختبار الإجهاد أو Stress Testing)، لكن الهدف هو نشوف كيف أداء التطبيق تحت ضغط متوقع. هل بصير بطيء؟ كم أقصى عدد من المستخدمين بيقدر يخدمهم بنفس الجودة؟ وين نقاط الضعف أو “عنق الزجاجة” (Bottlenecks) في نظامنا؟
باختصار، اختبار الحمل بجاوب على سؤال مهم: “هل تطبيقي جاهز للنجاح؟”. لأنه النجاح بيجي معاه مستخدمين كثار، والمستخدمين بيجيبوا ضغط. إذا أساساتك مش قوية، بيتك راح يوقع.
أنواع أخرى سريعة من اختبارات الأداء
- Stress Testing (اختبار الإجهاد): هنا الهدف هو نزيد الضغط فوق المتوقع لحتى نشوف متى وكيف النظام بنهار.
- Spike Testing (اختبار الذروة): محاكاة زيادة مفاجئة وكبيرة جدًا في عدد المستخدمين خلال فترة قصيرة جدًا.
- Endurance Testing (اختبار التحمل): تشغيل حمل متوسط على النظام لفترة طويلة جدًا (ساعات أو أيام) لاكتشاف مشاكل مثل تسريب الذاكرة (Memory Leaks).
لكن اليوم، تركيزنا راح يكون على اختبار الحمل، لأنه هو خط الدفاع الأول والأهم.
كيف تبدأ رحلتك مع اختبار الحمل؟ (الخطوات العملية)
الموضوع مش معقد زي ما البعض بفكر. خلينا نمشي خطوة بخطوة.
الخطوة الأولى: تحديد الأهداف والسيناريوهات (مش كلشي لازم نختبره)
قبل ما تكتب سطر كود واحد، لازم تسأل حالك: “شو اللي بدي أختبره بالضبط؟”. لا تضيع وقتك في اختبار كل صفحة وزر في التطبيق. ركز على المسارات الحرجة (Critical User Journeys)، وهي العمليات اللي بيقوم فيها معظم المستخدمين واللي بتأثر بشكل مباشر على تجربتهم.
أمثلة على المسارات الحرجة:
- في متجر إلكتروني: البحث عن منتج، إضافة المنتج للسلة، عملية الدفع.
- في شبكة اجتماعية: تسجيل الدخول، تصفح الـ Feed، كتابة منشور جديد.
- في نظام إدارة محتوى: الدخول للوحة التحكم، عرض المقالات.
بعد ما تحدد السيناريوهات، حدد أهداف أداء واضحة وقابلة للقياس. لا تقول “بدي التطبيق يكون سريع”. قول: “بدي متوسط زمن الاستجابة لصفحة المنتجات يكون أقل من 500ms تحت ضغط 200 مستخدم متزامن، وبنسبة أخطاء 0%”. الأرقام هي لغتنا كمطورين.
الخطوة الثانية: تجهيز بيئة الاختبار (اعزلها عن الإنتاج يا حبيب)
نصيحة من أخوك أبو عمر: إياك، ثم إياك، ثم إياك أن تجري اختبارات حمل ثقيلة على بيئة الإنتاج الحية (Production Server)! هذا مثل اختبار صلابة محرك سيارتك وهي ماشية على سرعة 120 كم/ساعة على الأوتوستراد. ممكن تسبب كارثة وتأثر على المستخدمين الحقيقيين.
الحل هو إنشاء بيئة اختبار (Staging/Testing Environment) تكون نسخة طبق الأصل عن بيئة الإنتاج قدر الإمكان:
- نفس مواصفات العتاد: نفس عدد أنوية المعالج (CPU)، نفس حجم الذاكرة (RAM).
- نفس البرمجيات: نفس إصدار نظام التشغيل، قاعدة البيانات، الـ Web Server.
- نفس البيانات (تقريبًا): استخدم نسخة من بيانات الإنتاج (بعد إزالة أي معلومات حساسة طبعًا) عشان يكون الاختبار واقعي. اختبار قاعدة بيانات فاضية ما إله معنى.
الخطوة الثالثة: اختيار الأداة المناسبة (العدة الصح لشغل صح)
في أدوات كثيرة في السوق، بعضها قديم وقوي مثل Apache JMeter، وبعضها حديث وموجه للمطورين. أنا شخصيًا صرت أميل كثيرًا لأداة اسمها k6.
ليش k6؟
- سهلة الاستخدام: بتكتب سكربتات الاختبار بلغة JavaScript، وهي لغة معظم المطورين بيعرفوها.
- أداء عالي: مكتوبة بلغة Go، وهذا بيعطيها قدرة عالية على توليد ضغط كبير من جهاز واحد.
- موجهة للمطورين: سهلة الإدماج مع أنظمة الـ CI/CD مثل GitHub Actions أو Jenkins.
مثال عملي: لنختبر “صفحة المنتجات” باستخدام k6
كلام نظري بكفي، خلينا نشوف شوية كود. تخيل عنا API Endpoint بجيب قائمة المنتجات: GET /api/products. بدنا نختبر كيف أداءه لما 100 مستخدم يطلبوه بنفس الوقت لمدة دقيقة.
كتابة السكريبت الأول (بسم الله)
أول شي، لازم تثبت k6 على جهازك (التعليمات موجودة على موقعهم الرسمي). بعدها، أنشئ ملف جديد اسمه test-products.js واكتب فيه الكود التالي:
import http from 'k6/http';
import { sleep, check } from 'k6';
// هاي هي إعدادات الاختبار اللي بنحدد فيها شكل الحمل
export const options = {
// vus اختصار لـ Virtual Users (المستخدمين الافتراضيين)
vus: 100,
// مدة الاختبار الإجمالية
duration: '60s',
};
// هذا هو السيناريو اللي راح ينفذه كل مستخدم افتراضي بشكل متكرر
export default function () {
// الخطوة 1: إرسال طلب GET للـ API Endpoint
const res = http.get('https://api.test.your-app.com/api/products');
// الخطوة 2: التحقق من صحة الاستجابة (خطوة مهمة جدًا)
// بدنا نتأكد إنه الطلب رجع بنجاح (status code 200)
check(res, {
'status was 200': (r) => r.status == 200,
'transaction time r.timings.duration < 800,
});
// الخطوة 3: محاكاة تفكير المستخدم
// المستخدم الحقيقي ما بضل يضغط F5 بدون توقف، لازم نحط فترة انتظار بسيطة
sleep(1); // انتظر ثانية واحدة قبل تكرار الطلب
}
لتشغيل الاختبار، افتح الـ Terminal في نفس المجلد واكتب الأمر:
k6 run test-products.js
تحليل النتائج: كيف تقرأ الأرقام؟
بعد انتهاء الاختبار، k6 راح يعطيك ملخص جميل ومليان أرقام. لا تخاف منها، راح أشرحلك أهمها:
✓ status was 200
✓ transaction time < 800ms
checks.........................: 100.00% ✓ 5824 ✗ 0
data_received..................: 4.8 MB 80 kB/s
data_sent......................: 524 kB 8.7 kB/s
http_req_blocked...............: avg=1.26ms min=2µs med=5µs max=68.12ms p(90)=9µs p(95)=11µs
http_req_connecting............: avg=312.2µs min=0s med=0s max=22.51ms p(90)=0s p(95)=0s
✓ http_req_duration..............: avg=250.46ms min=150.12ms med=230.8ms max=750.2ms p(90)=350.5ms p(95)=480.7ms
{ expected_response:true }...: avg=250.46ms min=150.12ms med=230.8ms max=750.2ms p(90)=350.5ms p(95)=480.7ms
✓ http_req_failed................: 0.00% ✓ 0 ✗ 5824
http_req_receiving.............: avg=112.3µs min=19µs med=80µs max=6.2ms p(90)=161µs p(95)=215µs
http_req_sending...............: avg=25.4µs min=7µs med=23µs max=1.2ms p(90)=38µs p(95)=45µs
http_req_tls_handshaking.......: avg=850.1µs min=0s med=0s max=45.6ms p(90)=0s p(95)=0s
http_req_waiting...............: avg=250.32ms min=150.04ms med=230.7ms max=750.1ms p(90)=350.3ms p(95)=480.5ms
http_reqs......................: 5824 97.05038/s
iteration_duration.............: avg=1.02s min=1.01s med=1.02s max=1.75s p(90)=1.03s p(95)=1.04s
iterations.....................: 5824 97.05038/s
vus............................: 100 min=100 max=100
vus_max........................: 100 min=100 max=100
أهم مؤشرين لازم تركز عليهم:
http_req_failed: نسبة الطلبات الفاشلة. هدفك دائمًا يكون هذا الرقم 0.00%. إذا شفت أي نسبة فشل، معناها النظام بدأ ينهار تحت الضغط.http_req_duration: هذا هو وقت استجابة الطلب. ركز على هاي القيم:avg: متوسط وقت الاستجابة. رقم جيد للمتابعة، لكنه خداع أحيانًا.p(95): أهم رقم في رأيي. هاي القيمة معناها “95% من الطلبات تمت في وقت أقل من هذا الرقم”. في مثالنا،p(95)=480.7msيعني 95% من المستخدمين حصلوا على استجابة في أقل من نصف ثانية، وهذا ممتاز! ليش هو أهم من المتوسط؟ لأنه المتوسط ممكن يتأثر بكم طلب سريع جدًا، لكن p(95) بيعطيك صورة عن تجربة معظم المستخدمين، مش بس المتوسط الحسابي.
إذا كانت هاي الأرقام ضمن الأهداف اللي حددتها في الخطوة الأولى، فمبروك! الـ Endpoint تبعك قوي. إذا كانت الأرقام سيئة (مثلًا p(95) كان 3000ms)، فهنا يبدأ العمل الحقيقي: تحليل “عنق الزجاجة”.
نصائح من “كيس” أبو عمر (خلاصة تجارب)
- ابدأ بسيطًا ثم تعقّد: لا تبدأ الاختبار بـ 1000 مستخدم. ابدأ بـ 10، ثم 50، ثم 100، 200 وهكذا. راقب كيف تتغير الأرقام مع زيادة الحمل. هذا يساعدك تكتشف نقطة الانهيار بدقة.
- الاختبار ليس مرة واحدة: أفضل ممارسة هي دمج اختبارات الحمل في عملية الـ CI/CD. شغل اختبار بسيط مع كل عملية دمج (merge) للكود، واختبار أثقل كل ليلة. هيك بتكتشف مشاكل الأداء أول بأول، مش ليلة الإطلاق.
- راقب النظام أثناء الاختبار: لا تكتفي بالنظر لنتائج k6. افتح شاشات مراقبة (Monitoring) للسيرفر نفسه أثناء الاختبار. راقب استهلاك الـ CPU، الـ Memory، الـ I/O تبع القرص الصلب، واستعلامات قاعدة البيانات البطيئة (Slow Queries). الأدوات مثل
htop,Prometheus,Grafana, أو خدمات مثلNew RelicوDatadogهي أصدقاؤك هنا. - اعرف عنق الزجاجة (The Bottleneck): هل المشكلة في استعلام قاعدة بيانات غير محسن (Missing Index)؟ هل هي مكتبة خارجية بطيئة؟ هل الـ Web Server إعداداته غير صحيحة؟ هل تحتاج لعمل Caching؟ الهدف من الاختبار هو إيجاد هذا “العنق” وتوسيعه.
الخلاصة: لا تنتظر الكارثة لتبني سفينتك 🚢
يا جماعة، القصة اللي حكيتها في البداية كانت مؤلمة، لكنها كانت أفضل درس تعلمته في مسيرتي. البرمجة مش بس كتابة كود شغال، البرمجة هي بناء أنظمة قوية وموثوقة تقدر تخدم المستخدمين في أحسن الظروف وأسوئها.
اختبار الحمل مش رفاهية أو خطوة إضافية. هو بوليصة تأمين لتطبيقك، هو اللي بيخليك تنام مرتاح بالليل وأنت عارف إن تطبيقك جاهز يستقبل ضيوفه الكرام، سواء كانوا عشرة أو عشرة آلاف.
نصيحتي الأخيرة: ابدأ اليوم. حتى لو تطبيقك لسا صغير، اكتب سكريبت اختبار بسيط. خليه يكبر مع تطبيقك. تذكر دائمًا، بناء الأساسات القوية أرخص وأسهل بكثير من ترميم بيت منهار.
الله يوفقكم في مشاريعكم، ويبعد عنكم جحيم الأعطال! 🙏