Производительность сети Node.js сильно страдает при нагрузочном тестировании на AWS

У меня есть следующий код Node.js, написанный как очень простой HTTP-сервер. Цель состоит в том, чтобы глотать большое количество запросов, содержащих данные base64, и записывать эти данные в S3 как изображение. Аспект S3-записи работает фантастически и не имеет проблем. Тем не менее, первоначальный запрос, похоже, требует чрезмерно длительного времени под нагрузкой.

server.js

http.createServer(function(req, res){ if (url.parse(req.url).pathname == '/processimage' && req.method.toLowerCase() == 'post') { var startTime = new Date(); var rawBody = ''; req.on('data', function(chunk) { rawBody += chunk; }); req.on('end', function() { console.log('REQUEST FINISHED: ' + (new Date() - startTime) + ' ms'); // Process image, upload to S3 res.writeHead(200); res.end('data'); } return; } else { // Other requests }
}).listen(1347);

Я также занимаюсь секцией обработки изображений, но он отлично работает и не имеет отношения к этому вопросу.

Чтобы проверить это, я написал сценарий, который POSTs тестирует данные, содержащие base64 размером приблизительно 500 тыс. Символов (оригинальные изображения 2-3 МБ). При тестировании локально все работает нормально. Мой выход:

REQUEST FINISHED: 9ms
REQUEST FINISHED: 23ms
REQUEST FINISHED: 18ms

и т.п.

Однако после развертывания кода в AWS в экземпляре x-large я вижу следующее:

REQUEST FINISHED: 499ms
REQUEST FINISHED: 2493ms
REQUEST FINISHED: 1784ms
REQUEST FINISHED: 3440ms
REQUEST FINISHED: 994ms
REQUEST FINISHED: 36043ms

По сути, при стресс-тестировании это примерно 1 из каждых 30 запросов, кажется, занимает 10+ секунд (даже 30+ секунды в некоторых случаях), чтобы пройти через конвейер запросов. Как вы можете видеть в моем коде, перед данными вычисляется нулевая обработка, поэтому это означает, что где-то между "req.on(" data ") и" req.on("end"), происходит массовая задержка.

Мой вопрос: есть ли какая-то обработка, происходящая между req.on('data') и req.on('end'), которая заставит этот POST занять так много времени? Возможно ли, что хост-машина по некоторым причинам задыхается от этих запросов (Ubuntu 12.04, x-large instance, 14-гигабайтная память, 4-кратные процессоры)?

1 ответ

Скорее всего, это займет некоторое время, чтобы увидеть, что происходит. У вас есть несколько запросов, каждый из которых имеет большие данные, которые будут обрабатываться несколькими кусками. Я бы сказал, что первое, что нужно сделать, - это регистрировать точно, когда обрабатывается каждый кусок от каждого запроса, и тогда вы можете понять, что происходит в порядке вещей и посмотреть, куда это ведет. Вот идея первого прохода при регистрации:

// helper function
logDelta(id, start, msg) { var delta = new Date() - start; console.log(id + ": (" + delta + ") - " + msg);
}
var reqCntr = 0;
http.createServer(function(req, res){ if (url.parse(req.url).pathname == '/processimage' && req.method.toLowerCase() == 'post') { // place an id on the request for logging purposes req.trackerID = reqCntr++; console.log(req.trackerID + ": Begin Request"); var startTime = new Date(); var rawBody = ''; var chunkCntr = 0; req.on('data', function(chunk) { logDelta(req.trackerID, startTime, "chunk(" + chunkCntr + "), length = " + chunk.length); ++chunkCntr; rawBody += chunk; }); req.on('end', function() { logDelta(req.trackerID, startTime, "Request Finished"); // Process image, upload to S3 res.writeHead(200); res.end('data'); } return; } else { // Other requests }
}).listen(1347);

Затем вам, возможно, придется выполнить некоторую обработку данных журнала, чтобы иметь возможность следить за временем каждого события в каждом отдельном запросе, особенно в длинных. Это, скорее всего, даст вам подсказку о том, где искать дальше.

FYI, в стеке есть куча разных мест, где вы можете столкнуться с узким местом. Если, например, вы запускаете данные на сервере быстрее, чем его можно обрабатывать (либо на уровне ОС, либо на уровне узла), тогда буферы TCP будут заполняться в какой-то момент, а входящие пакеты будут удалены или сокет будет помещен в какой-то контроль потока.

Если вы работаете на общем сервере, у вас может не быть доступа ко всему TCP-буфере.

Здесь схема, которая собирала бы всю историю для каждого соединения, а затем выводила бы ее в журнал сразу (облегчая рассечение одного соединения, но скрывая последовательность событий между различными соединениями).

var reqCntr = 0;
http.createServer(function(req, res){ var log = []; var id; var startTime = new Date(); // helper function logDelta(msg) { var delta = new Date() - startTime; log.push(id + ": (" + delta + ") - " + msg); } if (url.parse(req.url).pathname == '/processimage' && req.method.toLowerCase() == 'post') { // place an id on the request for logging purposes id = reqCntr++; log.push(id + ": Begin Request"); var rawBody = ''; var chunkCntr = 0; req.on('data', function(chunk) { logDelta("chunk(" + chunkCntr + "), length = " + chunk.length); ++chunkCntr; rawBody += chunk; }); req.on('end', function() { // dump connection history to console.log() logDelta("Request Finished"); console.log(log.join("/n")); // Process image, upload to S3 res.writeHead(200); res.end('data'); } return; } else { // Other requests }
}).listen(1347);

licensed under cc by-sa 3.0 with attribution.