لقد قمت بنشر نقطة نهاية. إنها تعمل في متصفحك. لكن ليس لديك أدنى فكرة عما يحدث عندما يصل إليها 400 شخص في وقت واحد. هل يبقى زمن الاستجابة ثابتًا، أم أن النسبة المئوية التاسعة والتسعين تتفجر؟ هل يمكن للجهاز استيعاب 1000 طلب في الثانية، أم أنه سينهار عند 300؟
wrk يجيب على ذلك. إنها أداة صغيرة سطر الأوامر ترسل الكثير من حركة مرور HTTP إلى عنوان URL وتُبلغ عن مدى سرعة استجابة الخادم تحت هذا الحمل.
ما هو wrk ومتى تستخدمه
wrk هي أداة حديثة لاختبار أداء HTTP. إنها تولد الحمل من جهاز واحد متعدد النواة وتقيس زمن الاستجابة ومعدل الطلبات التي يعيدها الخادم. تستخدم التعددية في المعالجة (multithreading) بالإضافة إلى حلقة أحداث قابلة للتوسع (epoll على Linux، kqueue على macOS)، لذلك يمكن لنسخة واحدة دفع الكثير من حركة المرور دون الحاجة إلى أسطول من أجهزة الحمل.
استخدم wrk عندما تريد أرقام أداء خام:
- كم عدد الطلبات في الثانية التي يمكن أن تتحملها نقطة النهاية هذه؟
- كيف يبدو زمن الاستجابة عند المتوسط مقارنة بالحد الأقصى (tail)؟
- هل يصمد الخادم لفترة زمنية مستمرة، أم أنه يتدهور بعد دقيقة؟
wrk هي أداة لاختبار الأداء، وليست مجموعة اختبارات. إنها تقيس السرعة. لا تتحقق مما إذا كان نص JSON صحيحًا، أو أن رمز الحالة هو 200، أو أن عقد API قد تم الالتزام به. حافظ على هذا التمييز في ذهنك. سنعود إليه قرب النهاية، لأنه يغير كيفية ملاءمة wrk في سير عمل الاختبار الحقيقي. إذا كنت تريد الصورة الأوسع أولاً، فإن هذا الدليل لاختبار تحميل API يغطي المفاهيم التي يطبقها wrk.
تثبيت wrk
macOS
Homebrew لديه ثنائي مسبق البناء، وهو المسار الأقل مقاومة:
brew install wrk
على Apple Silicon هذا مهم. البناء من المصدر يمكن أن يواجه مشاكل LuaJIT ARM64، لذا فإن ثنائي Homebrew يوفر عليك الصداع.
لينكس (البناء من المصدر)
لا توجد حزمة apt رسمية، لذا عليك بناؤها. قم بتثبيت سلسلة الأدوات ورؤوس OpenSSL أولاً:
sudo apt-get install build-essential libssl-dev git -y
ثم استنسخ وقم بالترجمة (compile):
git clone https://github.com/wg/wrk.git
cd wrk
make
سينتج عن ذلك ملف تنفيذي wrk في الدليل الحالي. انقله إلى مسار PATH الخاص بك حتى تتمكن من استدعائه من أي مكان:
sudo cp wrk /usr/local/bin
تأكد من أنه يعمل:
wrk --version
الأمر الأساسي
هذا هو شكل كل تشغيل لـ wrk:
wrk -t12 -c400 -d30s http://127.0.0.1:8080/index.html
تحدث أربعة أشياء. دعنا نفكك الأعلام.
-t, --threadsيحدد عدد مؤشرات ترابط نظام التشغيل التي يشغلها wrk. نقطة الانطلاق الجيدة هي مؤشر ترابط واحد لكل نواة وحدة معالجة مركزية (CPU core). هنا 12.-c, --connectionsيحدد إجمالي اتصالات HTTP المفتوحة عبر جميع مؤشرات الترابط. هنا يتم توزيع 400 اتصال عبر 12 مؤشر ترابط. الاتصالات هي كيف تحاكي العملاء المتزامنين.-d, --durationيحدد مدة تشغيل الاختبار. يقبل قيمًا مثل30s،2m، أو2h. هنا هي 30 ثانية.
علامتان أخريان ستستخدمهما باستمرار:
--latencyيطبع تفصيلاً لنسب مئوية لزمن الاستجابة. قم بتشغيل هذا في كل مرة تقريبًا. المتوسطات تخفي زمن الاستجابة المتأخر، والمتأخر هو ما يؤذي المستخدمين عادةً.--timeoutيسجل الطلب كمهلة إذا لم تصل استجابة خلال النافذة التي تحددها، على سبيل المثال--timeout 2s. بدونها، يمكن للاستجابات البطيئة أن تحرف أرقام زمن الاستجابة.
قد يبدو التشغيل الذي ستستخدمه غالبًا كالتالي:
wrk -t8 -c200 -d30s --latency http://localhost:3000/api/users
ثمانية مؤشرات ترابط، 200 اتصال، 30 ثانية، مع طباعة توزيع زمن الاستجابة الكامل في النهاية.
قراءة المخرجات
يطبع wrk تقريرًا موجزًا. إليك تشغيل حقيقي ضد خدمة صغيرة:
Running 5s test @ http://10.135.232.163:3000
2 threads and 5 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 3.82ms 2.64ms 26.68ms 85.81%
Req/Sec 550.90 202.40 0.98k 68.00%
5494 requests in 5.01s, 1.05MB read
Requests/sec: 1096.54
Transfer/sec: 215.24KB
اقرأها من الأسفل إلى الأعلى، لأن السطرين الأخيرين هما العنوان الرئيسي.
Requests/sec هو الإنتاجية (throughput): كم عدد الطلبات التي أكملها الخادم في الثانية في المتوسط. هنا 1,096. هذا هو الرقم الذي تقارنه عبر التشغيلات وعبر تغييرات الكود.
Transfer/sec هو عرض النطاق الترددي (bandwidth): كمية البيانات التي تم نقلها في الثانية. مفيد عندما تكون الحمولات كبيرة أو تشك في أنك مقيد بعرض النطاق الترددي بدلاً من وحدة المعالجة المركزية.
الآن جدول Thread Stats، الذي يصف التوزيع، وليس مجرد المتوسط:
- صف Latency: متوسط زمن الاستجابة كان 3.82ms، لكن الانحراف المعياري كان 2.64ms والحد الأقصى ارتفع إلى 26.68ms. الحد الأقصى المرتفع بالنسبة للمتوسط هو إشارة. بعض الطلبات بطيئة حتى لو كانت معظمها سريعة.
- صف Req/Sec: معدل الطلبات لكل مؤشر ترابط، مرة أخرى مع انتشاره.
يخبرك عمود +/- Stdev بالنسبة المئوية للعينات التي وقعت ضمن انحراف معياري واحد. تشير النسب المئوية الأقل إلى انتشار أوسع وأقل قابلية للتنبؤ.
السطر 5494 requests in 5.01s يؤكد الحجم الكلي الذي دفعه التشغيل بالفعل.
عند إضافة --latency، يطبع wrk كتلة نسب مئوية حتى تتمكن من رؤية الذيل مباشرة:
Latency Distribution
50% 3.21ms
75% 4.86ms
90% 7.09ms
99% 14.13ms
النسبة المئوية 99 هي الرقم الذي يجب مراقبته. إذا انتهى 99% من الطلبات في 14 مللي ثانية ولكن متوسطك هو 3.82 مللي ثانية، فإن واحدًا من كل مائة مستخدم ينتظر وقتًا أطول بكثير مما يوحي به المتوسط. المتوسطات تكذب بشأن الذيول. النسب المئوية لا تفعل ذلك.
إرسال طلبات POST ورؤوس مخصصة باستخدام نص Lua برمجي
بشكل افتراضي، يرسل wrk طلبات GET. لإرسال طلب POST، أو إضافة نص، أو تعيين رؤوس مخصصة، يمكنك تمرير نص Lua برمجي باستخدام -s.
أنشئ ملفًا باسم post.lua:
wrk.method = "POST"
wrk.body = '{"name": "Ada", "role": "engineer"}'
wrk.headers["Content-Type"] = "application/json"
ثلاثة حقول تقوم بالعمل. wrk.method يحدد فعل HTTP. wrk.body يحدد نص الطلب. wrk.headers هو جدول حيث كل مفتاح هو اسم رأس.
قم بتشغيله بتوجيه -s إلى النص البرمجي:
wrk -t4 -c100 -d30s -s post.lua --latency http://localhost:3000/api/users
لطلبات POST ذات الترميز النموذجي بدلاً من JSON، يحتوي مستودع wrk على هذا المثال بالضبط:
wrk.method = "POST"
wrk.body = "foo=bar&baz=quux"
wrk.headers["Content-Type"] = "application/x-www-form-urlencoded"
يمكنك أيضًا تعيين الرؤوس باستخدام علامة -H للحالات الأبسط، بدون نص برمجي:
wrk -t4 -c100 -d30s -H "Authorization: Bearer TOKEN123" --latency http://localhost:3000/api/protected
استخدم -H لرأس أو رأسين. استخدم نص Lua برمجي عندما تحتاج إلى نص، أو طريقة غير GET، أو منطق خاص بكل طلب.
القيود: wrk لا يتحقق من الصحة
هذا هو الجزء الذي يغفله الناس. يخبرك wrk بمدى سرعة استجابة الخادم. لكنه لا يخبرك ما إذا كانت الإجابة صحيحة.
وجّه wrk إلى نقطة نهاية تُرجع HTTP 500 في كل طلب، وستحصل على تقرير يبدو نظيفًا مع عدد طلبات في الثانية مرتفع. wrk يحسب تبادلاً كاملاً لـ HTTP. لا يتحقق من رمز الحالة، ولا يتحقق من صحة نص الاستجابة مقابل مخطط، ولا يؤكد أن API قام بما كان من المفترض أن يفعله. يمكن أن تبدو الأخطاء سريعة حتى، لأن الخادم الذي يرفض الطلبات مبكرًا يقوم بعمل أقل لكل طلب.
لذلك يجيب wrk على سؤال "هل هو سريع بما فيه الكفاية تحت الحمل؟". لا يمكنه الإجابة على سؤال "هل هو صحيح؟" كلا السؤالين مهمان، ويحتاجان إلى أدوات مختلفة. رقم الحمل على نقطة نهاية معطلة هو رقم لا يجب أن تثق به. هذا هو بالضبط سبب قيام الفرق بمزاوجة أداة اختبار الأداء مع مجموعة اختبارات وظيفية. إحداهما تثبت السرعة. والأخرى تثبت السلوك.
مكان Apidog والاختبار الوظيفي
سير العمل النظيف يتكون من طبقتين، يتم تشغيلهما بالترتيب.
أولاً، تحقق من السلوك. قبل أن تهتم بمدى سرعة نقطة النهاية، تأكد من صحتها. في Apidog، يمكنك بناء سيناريوهات اختبار ترسل طلبات حقيقية وتتحقق مما يعود: رموز الحالة، وحقول JSON، ومخطط الاستجابة، ومنطق العمل. يمكنك ربط الطلبات، وتمرير البيانات بين الخطوات، وتشغيل نفس السيناريو عبر البيئات. هذه هي الطبقة التي تلتقط الخطأ 500 الذي يقوم wrk باختباره بسعادة.
ثم، قم باختبار الأداء (benchmark throughput). بمجرد التحقق من السلوك، قم بتشغيل wrk مقابل نفس نقاط النهاية لترى كيف تصمد تحت التزامن والحمل المستمر. يحتوي Apidog أيضًا على اختبار أداء مدمج إذا كنت تفضل الاحتفاظ بالعمل الوظيفي وتحميل العمل في مكان واحد، ولكن wrk هي أداة مخصصة رائعة لاختبار الأداء من سطر الأوامر.
يتم تشغيل الطبقة الوظيفية في CI، وليس فقط على جهاز الكمبيوتر المحمول الخاص بك. واجهة سطر الأوامر (CLI) الخاصة بـ Apidog لا تحتوي على واجهة رسومية (headless)، لذلك تندمج في أي خطوة من خطوات البايبلاين يمكنها تشغيل Node. قم بتثبيتها:
npm install -g apidog-cli
ثم قم بتشغيل سيناريو اختبار محفوظ أو مجموعة اختبار بواسطة المعرف (ID):
apidog run \
--access-token "$APIDOG_ACCESS_TOKEN" \
-t <scenarioOrSuiteId> \
-e <environmentId> \
-r cli,html,junit
-t هو معرف السيناريو أو المجلد أو المجموعة المطلوب تشغيلها. -e هو معرف البيئة. -r يختار تنسيقات التقرير، واحد أو أكثر من cli، html، json، و junit. يندمج إخراج JUnit مباشرة في معظم أنظمة CI لبوابة النجاح/الفشل. للتشغيلات المستندة إلى البيانات، أضف -d (أو --iteration-data) مع مسار ملف أو معرف بيانات الاختبار لتكرار نفس السيناريو على العديد من صفوف الإدخال.
يقوم CLI بتشغيل سيناريوهات ومجموعات Apidog المحفوظة. إنه يعمل بدون واجهة رسومية، وليس مرسل طلبات تفاعليًا، وليس مولدًا للحمل. إنه بوابة الصحة. wrk هو مقياس السرعة. قم بتشغيل بوابة الصحة في خط الأنابيب الخاص بك (انظر هذا دليل CLI CI/CD أو دليل GitHub Actions للتكوين الذي يمكن نسخه ولصقه)، ثم قم بالاختبار باستخدام wrk عندما تحتاج إلى أرقام الإنتاجية. مرجع CLI الكامل يغطي باقي العلامات.
الأسئلة الشائعة
ما الفرق بين wrk و ab (ApacheBench)؟ كلاهما يرسل حملاً HTTP ويُبلغ عن الطلبات في الثانية. wrk متعدد الخيوط ويستخدم حلقة أحداث، لذلك يولد حملاً أكبر من جهاز واحد ويتعامل مع التزامن العالي بشكل أفضل. ab أحادي الخيط. للحمل الثقيل من جهاز واحد، يتوسع wrk عادةً بشكل أكبر. لا يتحقق أي منهما من صحة الاستجابة.
كم عدد الخيوط والاتصالات التي يجب أن أستخدمها؟ ابدأ بخيط واحد لكل نواة وحدة معالجة مركزية، واضبط الاتصالات على مستوى التزامن الذي ترغب في محاكاته. إذا كان لديك 8 أنوية وتريد محاكاة 200 عميل متزامن، جرب -t8 -c200. راقب جهاز العميل. إذا كان wrk نفسه مقيدًا بوحدة المعالجة المركزية، فإن أرقامك تعكس حد مولد الحمل، وليس الخادم. ارفع الاتصالات حتى تتوقف الإنتاجية عن الارتفاع.
هل يمكن لـ wrk اختبار نقاط نهاية HTTPS؟ نعم. وجّهه إلى عنوان URL يبدأ بـ https:// وسيتعامل wrk مع TLS. هذا هو سبب احتياج بناء Linux لـ libssl-dev. تُضيف مصافحات TLS تكلفة وحدة المعالجة المركزية على كلا الطرفين، لذا توقع إنتاجية خام أقل ضد HTTPS مقارنة بـ HTTP العادي.
هل يتحقق wrk من نص الاستجابة أو رمز الحالة؟ لا. wrk يحسب تبادلات HTTP المكتملة ويقيس التوقيت. لا يتحقق من رموز الحالة أو النصوص، لذا فإن نقطة نهاية تُرجع أخطاء يمكن أن تستمر في إظهار عدد طلبات مرتفع في الثانية. استخدم مجموعة اختبارات وظيفية، مثل تلك التي تُجرى عبر واجهة سطر الأوامر Apidog، للتحقق من الصحة، ثم استخدم wrk لقياس الإنتاجية.
كم يجب أن يستمر اختبار الحمل؟ لفترة كافية لتجاوز تأثيرات الإحماء مثل ذاكرة التخزين المؤقت الباردة وتجميع JIT. بضع ثوانٍ جيدة لإجراء فحص سريع، ولكن من 30 ثانية إلى بضع دقائق تعطي أرقامًا أكثر استقرارًا وتظهر التدهور الذي يظهر فقط تحت الحمل المستمر. استخدم -d30s كإعداد افتراضي معقول وقم بزيادته عندما تبحث عن تسربات بطيئة.
