זיהוי בעיות ביצועים עם Apache Bench
אפאצ'י בנצ' הוא כלי חינמי ופשוט שעוזר לבדוק איך האתר שלכם מתפקד תחת עומס. אני אוהב את Apache Bench כי הוא ממש פשוט לשימוש ועוזר למצוא בעיות ביצועים נפוצות בשרת. החיסרון המרכזי שלו הוא היעדר התמיכה ב Sessions ולכן הוא לא מאוד שימושי כשאתם רוצים לוודא שהשרת ישרוד בעומס של העולם האמיתי.
השימוש העיקרי ב ab הוא לבדוק שנתיב מסוים שאתם חושדים בו שעושה בעיות באמת עושה בעיות.
1. התחברות לנתיב ובדיקה שהכל עובד
יש לנו נתיב שאנחנו חושדים שעושה בעיות ורוצים לקבל יותר מידע. קודם כל נפעיל את ab עם הנתיב וכל הפרמטרים ונראה שאנחנו מצליחים להתחבר ולקבל תשובה נכונה מהשרת:
ab -v 4 http://localhost:3000/
הפלט קצת ארוך ואצלי ביישום נראה כך:
This is ApacheBench, Version 2.3 <$Revision: 1826891 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/
Benchmarking localhost (be patient)...INFO: GET header ==
---
GET / HTTP/1.0
Host: localhost:3000
User-Agent: ApacheBench/2.3
Accept: */*
---
LOG: header received:
HTTP/1.1 200 OK
X-Powered-By: Express
Content-Type: text/html; charset=utf-8
Content-Length: 1269
ETag: W/"4f5-Mre6pRuFJfReQtSirkxsmAGixI8"
Date: Sun, 30 Jun 2019 13:14:44 GMT
Connection: close
<!DOCTYPE html><html><head><title>Express</title><link rel="stylesheet" href="/stylesheets/style.css"></head><body><h1>Express</h1><p>Welcome to Express</p><pre><code>var createError = require('http-errors');
var express = require('express');
var path = require('path');
var cookieParser = require('cookie-parser');
var logger = require('morgan');
var indexRouter = require('./routes/index');
var usersRouter = require('./routes/users');
var app = express();
// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'jade');
app.use(logger('dev'));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));
app.use('/', indexRouter);
app.use('/users', usersRouter);
// catch 404 and forward to error handler
app.use(function(req, res, next) {
next(createError(404));
});
// error handler
app.use(function(err, req, res, next) {
// set locals, only providing error in development
res.locals.message = err.message;
res.locals.error = req.app.get('env') === 'development' ? err : {};
// render the error page
res.status(err.status || 500);
res.render('error');
});
module.exports = app;
</code></pre></body></html>
LOG: Response code = 200
..done
Server Software:
Server Hostname: localhost
Server Port: 3000
Document Path: /
Document Length: 1269 bytes
Concurrency Level: 1
Time taken for tests: 0.006 seconds
Complete requests: 1
Failed requests: 0
Total transferred: 1472 bytes
HTML transferred: 1269 bytes
Requests per second: 158.18 [#/sec] (mean)
Time per request: 6.322 [ms] (mean)
Time per request: 6.322 [ms] (mean, across all concurrent requests)
Transfer rate: 227.38 [Kbytes/sec] received
Connection Times (ms)
min mean[+/-sd] median max
Connect: 0 0 0.0 0 0
Processing: 6 6 0.0 6 6
Waiting: 6 6 0.0 6 6
Total: 6 6 0.0 6 6
אנחנו יכולים לראות את תשובת השרת התקינה וגם את הזמן שלקח עד שקיבלנו אותה.
2. יצירת עומס על הנתיב
עכשיו נוכל להגדיל את העומס כדי לראות מתי השרת נשבר. המתג n קובע כמה בקשות לשלוח והמתג c קובע כמה מהן לשלוח במקביל. סך הכל הפקודה הבאה תשלח מאה בקשות מחולקות לקבוצות של עשר בקשות במקביל:
ab -c 10 -n 100 http://localhost:3000/
והפלט:
This is ApacheBench, Version 2.3 <$Revision: 1826891 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/
Benchmarking localhost (be patient).....done
Server Software:
Server Hostname: localhost
Server Port: 3000
Document Path: /
Document Length: 1269 bytes
Concurrency Level: 10
Time taken for tests: 0.358 seconds
Complete requests: 100
Failed requests: 0
Total transferred: 147200 bytes
HTML transferred: 126900 bytes
Requests per second: 279.24 [#/sec] (mean)
Time per request: 35.812 [ms] (mean)
Time per request: 3.581 [ms] (mean, across all concurrent requests)
Transfer rate: 401.40 [Kbytes/sec] received
Connection Times (ms)
min mean[+/-sd] median max
Connect: 0 0 0.3 0 3
Processing: 4 34 7.3 33 47
Waiting: 4 29 7.2 29 42
Total: 5 34 7.2 33 48
Percentage of the requests served within a certain time (ms)
50% 33
66% 38
75% 40
80% 40
90% 43
95% 46
98% 48
99% 48
100% 48 (longest request)
בינתיים זה לא היה כל כך נורא - אנחנו רואים שכל הבקשות קיבלו תשובה, ש 50% מהבקשות קיבלו תשובה תוך 33 מילי שניות ו 100% מהבקשות קיבלו תשובה תוך פחות מ 50 מילי שניות.
אבל אם מעלים את העומס כבר אפשר לראות את הבעיה:
ab -c 100 -n 1000 http://localhost:3000/
והפלט הפעם:
This is ApacheBench, Version 2.3 <$Revision: 1826891 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/
Benchmarking localhost (be patient)
Completed 100 requests
Completed 200 requests
Completed 300 requests
Completed 400 requests
Completed 500 requests
Completed 600 requests
Completed 700 requests
Completed 800 requests
Completed 900 requests
Completed 1000 requests
Finished 1000 requests
Server Software:
Server Hostname: localhost
Server Port: 3000
Document Path: /
Document Length: 1269 bytes
Concurrency Level: 100
Time taken for tests: 3.152 seconds
Complete requests: 1000
Failed requests: 0
Total transferred: 1472000 bytes
HTML transferred: 1269000 bytes
Requests per second: 317.31 [#/sec] (mean)
Time per request: 315.153 [ms] (mean)
Time per request: 3.152 [ms] (mean, across all concurrent requests)
Transfer rate: 456.13 [Kbytes/sec] received
Connection Times (ms)
min mean[+/-sd] median max
Connect: 0 1 1.0 1 4
Processing: 21 303 82.1 284 512
Waiting: 17 241 67.8 241 508
Total: 21 304 82.7 285 514
Percentage of the requests served within a certain time (ms)
50% 285
66% 323
75% 338
80% 342
90% 506
95% 507
98% 508
99% 508
100% 514 (longest request)
אנחנו רואים שככל שיש יותר בקשות במקביל כך הזמן שלוקח לענות לכל בקשה עולה בצורה משמעותית.
כשעולים למאה בקשות במקביל הסיפור רק נהיה עוד יותר קשוח ואנחנו מקבלים:
$ ab -c 100 -n 1000 http://localhost:3000/
This is ApacheBench, Version 2.3 <$Revision: 1826891 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/
Benchmarking localhost (be patient)
Completed 100 requests
Completed 200 requests
Completed 300 requests
Completed 400 requests
Completed 500 requests
Completed 600 requests
Completed 700 requests
Completed 800 requests
Completed 900 requests
Completed 1000 requests
Finished 1000 requests
Server Software:
Server Hostname: localhost
Server Port: 3000
Document Path: /
Document Length: 197 bytes
Concurrency Level: 100
Time taken for tests: 20.287 seconds
Complete requests: 1000
Failed requests: 0
Total transferred: 398000 bytes
HTML transferred: 197000 bytes
Requests per second: 49.29 [#/sec] (mean)
Time per request: 2028.735 [ms] (mean)
Time per request: 20.287 [ms] (mean, across all concurrent requests)
Transfer rate: 19.16 [Kbytes/sec] received
Connection Times (ms)
min mean[+/-sd] median max
Connect: 0 1 0.8 0 4
Processing: 29 1934 309.9 2007 2833
Waiting: 24 1844 335.5 1942 2769
Total: 29 1935 309.3 2008 2834
WARNING: The median and mean for the initial connection time are not within a normal deviation
These results are probably not that reliable.
Percentage of the requests served within a certain time (ms)
50% 2008
66% 2036
75% 2059
80% 2071
90% 2132
95% 2201
98% 2335
99% 2341
100% 2834 (longest request)
במצבים כאלה נרצה להסתכל על קוד השרת ולראות איך לשפר את החוויה של יותר גולשים במקביל. אם אפשר נרצה להוסיף מנגנוני Caching או להעביר חלק מהעבודה בצד השרת לרקע או לתהליך אחר.