performance - Node.js más lento que Apache
apache2 (6)
Solicitudes dinámicas
node.js es muy bueno en el manejo de solicitudes dinámicas pequeñas (que pueden ser suspendidas / largas). Pero no es bueno para manejar buffers grandes. Ryan Dahl (Author node.js) explicó esta una de sus presentaciones . Te recomiendo que estudies estas diapositivas. También vi esto en línea en algún lado.
Recolector de basura
Como se puede ver en la diapositiva (13 de 45), es malo en los buffers grandes.
Diapositiva 15 de 45:
V8 tiene un recolector de basura generacional. Mueve los objetos aleatoriamente. Nodo no puede obtener un puntero a los datos de cadena sin procesar para escribir en el socket.
Usar buffer
Diapositiva 16 de 45
Usando el nuevo objeto Buffer de Node, los resultados cambian.
Todavía no es tan bueno como por ejemplo nginx, pero mucho mejor. Además, estas diapositivas son bastante viejas, por lo que probablemente Ryan haya mejorado esto.
CDN
Aún así, no creo que deba usar node.js para alojar archivos estáticos. Probablemente sea mejor que los aloje en un CDN que está optimizado para alojar archivos estáticos. Algunos CDN populares (algunos incluso gratuitos) a través de WIKI.
NGinx (+ Memcached)
Si no quiere usar CDN para alojar sus archivos estáticos, le recomiendo usar Nginx con memcached, que es muy rápido.
Estoy comparando el rendimiento de Node.js (0.5.1-pre) vs Apache (2.2.17) para un escenario muy simple: servir un archivo de texto.
Aquí está el código que uso para el servidor de nodos:
var http = require(''http'')
, fs = require(''fs'')
fs.readFile(''/var/www/README.txt'',
function(err, data) {
http.createServer(function(req, res) {
res.writeHead(200, {''Content-Type'': ''text/plain''})
res.end(data)
}).listen(8080, ''127.0.0.1'')
}
)
Para Apache solo estoy usando la configuración predeterminada que acompaña a Ubuntu 11.04
Al ejecutar Apache Bench con los siguientes parámetros contra Apache
ab -n10000 -c100 http://127.0.0.1/README.txt
Obtengo los siguientes tiempos de ejecución:
Time taken for tests: 1.083 seconds
Complete requests: 10000
Failed requests: 0
Write errors: 0
Total transferred: 27630000 bytes
HTML transferred: 24830000 bytes
Requests per second: 9229.38 [#/sec] (mean)
Time per request: 10.835 [ms] (mean)
Time per request: 0.108 [ms] (mean, across all concurrent requests)
Transfer rate: 24903.11 [Kbytes/sec] received
Connection Times (ms)
min mean[+/-sd] median max
Connect: 0 0 0.8 0 9
Processing: 5 10 2.0 10 23
Waiting: 4 10 1.9 10 21
Total: 6 11 2.1 10 23
Percentage of the requests served within a certain time (ms)
50% 10
66% 11
75% 11
80% 11
90% 14
95% 15
98% 18
99% 19
100% 23 (longest request)
Cuando se ejecuta el banco Apache contra la instancia del nodo , estos son los tiempos de ejecución:
Time taken for tests: 1.712 seconds
Complete requests: 10000
Failed requests: 0
Write errors: 0
Total transferred: 25470000 bytes
HTML transferred: 24830000 bytes
Requests per second: 5840.83 [#/sec] (mean)
Time per request: 17.121 [ms] (mean)
Time per request: 0.171 [ms] (mean, across all concurrent requests)
Transfer rate: 14527.94 [Kbytes/sec] received
Connection Times (ms)
min mean[+/-sd] median max
Connect: 0 0 0.9 0 8
Processing: 0 17 8.8 16 53
Waiting: 0 17 8.6 16 48
Total: 1 17 8.7 17 53
Percentage of the requests served within a certain time (ms)
50% 17
66% 21
75% 23
80% 25
90% 28
95% 31
98% 35
99% 38
100% 53 (longest request)
Que es claramente más lento que Apache. Esto es especialmente sorprendente si se tiene en cuenta el hecho de que Apache está haciendo muchas otras cosas, como el registro, etc.
¿Lo estoy haciendo mal? ¿O es Node.js realmente más lento en este escenario?
Editar 1 : Noto que la concurrencia del nodo es mejor: al aumentar un número de solicitudes simultáneas a 1000, Apache comienza a descartar algunas de ellas, mientras que el nodo funciona bien sin conexiones caídas.
El resultado de su punto de referencia puede cambiar a favor de node.js si aumenta la concurrencia y utiliza la memoria caché en node.js
Un código de muestra del libro "Node Cookbook":
var http = require(''http'');
var path = require(''path'');
var fs = require(''fs'');
var mimeTypes = {
''.js'' : ''text/javascript'',
''.html'': ''text/html'',
''.css'' : ''text/css''
} ;
var cache = {};
function cacheAndDeliver(f, cb) {
if (!cache[f]) {
fs.readFile(f, function(err, data) {
if (!err) {
cache[f] = {content: data} ;
}
cb(err, data);
});
return;
}
console.log(''loading '' + f + '' from cache'');
cb(null, cache[f].content);
}
http.createServer(function (request, response) {
var lookup = path.basename(decodeURI(request.url)) || ''index.html'';
var f = ''content/''+lookup;
fs.exists(f, function (exists) {
if (exists) {
fs.readFile(f, function(err,data) {
if (err) { response.writeHead(500);
response.end(''Server Error!''); return; }
var headers = {''Content-type'': mimeTypes[path.extname(lookup)]};
response.writeHead(200, headers);
response.end(data);
});
return;
}
response.writeHead(404); //no such file found!
response.end(''Page Not Found!'');
});
En este escenario, Apache probablemente está haciendo sendfile que da como resultado que kernel envíe fragmentos de datos de memoria (en caché por el controlador fs) directamente al socket. En el caso del nodo, hay una sobrecarga en la copia de datos en el espacio de usuario entre v8, libeio y kernel (consulte this gran artículo sobre el uso de sendfile en el nodo)
Hay muchos escenarios posibles en los que el nodo superará a Apache, como ''enviar flujo de datos con velocidad lenta constante a tantas conexiones tcp como sea posible''
En los puntos de referencia a continuación,
Apache:
$ apache2 -version
Server version: Apache/2.2.17 (Ubuntu)
Server built: Feb 22 2011 18:35:08
PHP APC cache / accelerator está instalado.
Prueba de funcionamiento en mi computadora portátil, un Sager NP9280 con Core I7 920, 12G de RAM.
$ uname -a
Linux presto 2.6.38-8-generic #42-Ubuntu SMP Mon Apr 11 03:31:24 UTC 2011 x86_64 x86_64 x86_64 GNU/Linux
KUbuntu natty
En realidad, todo lo que hace aquí es hacer que el sistema copie datos entre memorias intermedias en la memoria, en espacios de direcciones de procesos diferentes: la memoria caché de disco significa que realmente no está tocando el disco y está utilizando conectores locales.
Por lo tanto, cuantas menos copias haya que hacer por solicitud, más rápido irá.
Editar: sugerí que agregue el almacenamiento en caché, pero de hecho ahora veo que ya está haciendo eso: lee el archivo una vez, luego inicia el servidor y devuelve el mismo búfer cada vez.
¿Ha intentado anexar la parte del encabezado a los datos del archivo una vez por adelantado, por lo que solo tiene que hacer una sola operación de escritura para cada solicitud?
$ cat /var/www/test.php
<?php
for ($i=0; $i<10; $i++) {
echo "hello, world/n";
}
$ ab -r -n 100000 -k -c 50 http://localhost/test.php
This is ApacheBench, Version 2.3 <$Revision: 655654 $>
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 10000 requests
Completed 20000 requests
Completed 30000 requests
Completed 40000 requests
Completed 50000 requests
Completed 60000 requests
Completed 70000 requests
Completed 80000 requests
Completed 90000 requests
Completed 100000 requests
Finished 100000 requests
Server Software: Apache/2.2.17
Server Hostname: localhost
Server Port: 80
Document Path: /test.php
Document Length: 130 bytes
Concurrency Level: 50
Time taken for tests: 3.656 seconds
Complete requests: 100000
Failed requests: 0
Write errors: 0
Keep-Alive requests: 100000
Total transferred: 37100000 bytes
HTML transferred: 13000000 bytes
Requests per second: 27350.70 [#/sec] (mean)
Time per request: 1.828 [ms] (mean)
Time per request: 0.037 [ms] (mean, across all concurrent requests)
Transfer rate: 9909.29 [Kbytes/sec] received
Connection Times (ms)
min mean[+/-sd] median max
Connect: 0 0 0.1 0 3
Processing: 0 2 2.7 0 29
Waiting: 0 2 2.7 0 29
Total: 0 2 2.7 0 29
Percentage of the requests served within a certain time (ms)
50% 0
66% 2
75% 3
80% 3
90% 5
95% 7
98% 10
99% 12
100% 29 (longest request)
$ cat node-test.js
var http = require(''http'');
http.createServer(function (req, res) {
res.writeHead(200, {''Content-Type'': ''text/plain''});
res.end(''Hello World/n'');
}).listen(1337, "127.0.0.1");
console.log(''Server running at http://127.0.0.1:1337/'');
$ ab -r -n 100000 -k -c 50 http://localhost:1337/
This is ApacheBench, Version 2.3 <$Revision: 655654 $>
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 10000 requests
Completed 20000 requests
Completed 30000 requests
Completed 40000 requests
Completed 50000 requests
Completed 60000 requests
Completed 70000 requests
Completed 80000 requests
Completed 90000 requests
Completed 100000 requests
Finished 100000 requests
Server Software:
Server Hostname: localhost
Server Port: 1337
Document Path: /
Document Length: 12 bytes
Concurrency Level: 50
Time taken for tests: 14.708 seconds
Complete requests: 100000
Failed requests: 0
Write errors: 0
Keep-Alive requests: 0
Total transferred: 7600000 bytes
HTML transferred: 1200000 bytes
Requests per second: 6799.08 [#/sec] (mean)
Time per request: 7.354 [ms] (mean)
Time per request: 0.147 [ms] (mean, across all concurrent requests)
Transfer rate: 504.62 [Kbytes/sec] received
Connection Times (ms)
min mean[+/-sd] median max
Connect: 0 0 0.1 0 3
Processing: 0 7 3.8 7 28
Waiting: 0 7 3.8 7 28
Total: 1 7 3.8 7 28
Percentage of the requests served within a certain time (ms)
50% 7
66% 9
75% 10
80% 11
90% 12
95% 14
98% 16
99% 17
100% 28 (longest request)
$ node --version
v0.4.8