Node.js 比 Apache 慢

Posted

技术标签:

【中文标题】Node.js 比 Apache 慢【英文标题】:Node.js slower than Apache 【发布时间】:2011-10-01 20:02:13 【问题描述】:

我正在比较 Node.js (0.5.1-pre) 与 Apache (2.2.17) 的性能,这是一个非常简单的场景 - 提供文本文件。

这是我用于节点服务器的代码:

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')
    
)

对于 Apache,我只是使用 Ubuntu 11.04 附带的任何默认配置

针对 Apache

使用以下参数运行 Apache Bench 时
ab -n10000 -c100 http://127.0.0.1/README.txt

我得到以下运行时:

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)

当针对 node 实例运行 Apache bench 时,这些是运行时:

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)

这显然比 Apache 慢。如果您考虑到 Apache 正在做很多其他事情,例如日志记录等,这尤其令人惊讶。

我做错了吗?或者 Node.js 在这种情况下真的慢吗?

编辑 1:我确实注意到节点的并发性更好 - 当同时请求的数量增加到 1000 时,Apache 开始丢弃其中的一些,而节点工作正常,没有连接丢失。

【问题讨论】:

您是否更新了代码以使用缓冲区?你有数字吗?如果可以,请您提供它们以供使用,因为我对这些数字有点好奇。 @Alfred,对不起,我没有用缓冲区重新测试。 哈哈好吧。没问题。有时间我会试试的。想知道... 哈哈,有了缓冲区,这些数字变得更糟:)。但话又说回来,我们不应该只是像我猜的那样进行基准测试。你应该像你一样检查内存、cpu 并增加并发请求的数量。另外我想指出的是,围攻比 AB 更受欢迎,而且当我真正攻击(-b)服务器时,围攻我得到更快的数字.. 另外我想指出 node.js 是单线程的,而 apacha 不是。当你把它放在 nginx 或其他东西后面时,你会得到更好的性能......最后但并非最不重要的一点是 node.js 在长时间运行(挂起)请求方面特别好。 Apache 在这方面真的很糟糕! 【参考方案1】:

实际上,您在这里所做的只是让系统在内存中的缓冲区之间、不同进程的地址空间中复制数据 - 磁盘缓存意味着您并没有真正接触磁盘,而是使用本地套接字。

因此,每个请求必须完成的副本越少,速度就越快。

编辑:我建议添加缓存,但实际上我现在看到您已经这样做了 - 您读取文件一次,然后启动服务器并每次发送回相同的缓冲区。

您是否尝试过预先将标头部分附加到文件数据中,这样您只需为每个请求执行一次写入操作?

【讨论】:

是的,我尝试的第一个版本是每次都读取文件,而且速度更慢。 重新单写操作——你真的可以用节点做吗?【参考方案2】:

在这种情况下,Apache 可能正在执行sendfile,这导致内核将内存数据块(由 fs 驱动程序缓存)直接发送到套接字。在 node 的情况下,在 v8、libeio 和内核之间复制用户空间中的数据会产生一些开销(请参阅this 关于在 node 中使用 sendfile 的精彩文章)

在很多可能的情况下,node 的性能都会优于 Apache,例如“以恒定的低速向尽可能多的 tcp 连接发送数据流”

【讨论】:

【参考方案3】:

动态请求

node.js 非常擅长处理大量小的动态请求(可以是挂起/长轮询)。但它不擅长处理大缓冲区。 Ryan Dahl(作者 node.js)解释了his presentations 中的这一点。我建议您学习这些幻​​灯片。我也在某处网上看过这个。

垃圾收集器

从幻灯片中可以看出(45 中的 13),它不适合大缓冲区。

幻灯片 15 从 45:

V8 有一个世代垃圾 集电极。移动物体 随机。节点无法获得指向 要写入套接字的原始字符串数据。

使用缓冲区

幻灯片 16 从 45

使用 Node 的新 Buffer 对象, 结果变化。

仍然不如 nginx,但要好得多。这些幻灯片也很老了,所以可能 Ryan 甚至改进了这一点。

CDN

我仍然认为您不应该使用 node.js 来托管静态文件。您可能最好将它们托管在针对托管静态文件进行了优化的 CDN 上。 Some popular CDN's(有些甚至免费)通过 WIKI。

NGinx(+Memcached)

如果您不想使用 CDN 来托管您的静态文件,我建议您改用 Nginx with memcached,这非常快。

【讨论】:

nodejs.org/jsconf2010.pdf 404: 找不到页面 ENOENT: 没有这样的文件或目录【参考方案4】:
$ 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

【讨论】:

【参考方案5】:

在以下基准测试中,

阿帕奇:

$ apache2 -version
Server version: Apache/2.2.17 (Ubuntu)
Server built:   Feb 22 2011 18:35:08

PHP APC 缓存/加速器已安装。

在我的笔记本电脑上测试运行,Sager NP9280,Core I7 920,12G 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 整洁

【讨论】:

【参考方案6】:

如果您增加并发性并在 node.js 中使用缓存,您的基准测试结果可能会发生有利于 node.js 的变化

《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!');
);

【讨论】:

以上是关于Node.js 比 Apache 慢的主要内容,如果未能解决你的问题,请参考以下文章

与 Apache 相比,Node.js 的性能如何?

ApacheNginx 与 Node.js 之争 —— WordPress 与 Ghost 的性能大对决

深入浅出Node.js 模块机制

PHP 与 Node.js - 在 Node.js 中使用 Jade 的 HTML 渲染速度会变慢吗?

为啥 WebSocket 实现在传输多个文件时比 HTTP/2 Push 慢? (Node.js / 去)

仅使用 Node.js 与将 Node.js 与 Apache/Nginx 一起使用