PHP VS NODE.JS: THE REAL STATISTICS一文用PHP和Node.js实现文件读写的案例展示两者性能对比, 两者相差14倍之多。究其原因还是Node.JS的异步IO。
I/O是Web应用典型的消耗瓶颈,当一个请求达到Apache web服务器时,它将动态脚本内容传递到PHP解释器 ,如果PHP脚本这时向磁盘或数据库读写数据,因为磁盘和数据库比较慢,就会造成整个请求链条的最慢一个环节。当你调用 PHP 函数 file_get_contents(), 整个线程就堵塞住了,直至等到从磁盘或数据库读写数据成功。在这段时间,服务器不能做任何事情。
如果这时还有其他用户同时发出请求,对不起,这些用户请求必须等待了,因为已经没有线程来处理这些用户请求,线程都堵塞在I/O了。
( Node.js的异步机制并不是强在Http服务器 IO内部机制上,并不如同NIO非堵塞IO那种机制,它的异步强在协调多个堵塞源)
node.js唯一卖点在这里,它能对所有IO实现异步,一旦文件获得(fs.readFile),服务器线程就空闲被其他函数调用,一旦整个I/O读写全部完成了,node会在读写完成后再调用一个回调函数 (这是之前通过fs.readFile一起传递过来的)进行后续相关扫尾工作。
下面两个脚本分别是测试脚本,功能是:
1. 接受一个请求
2. 产生一个随机数
3. 将随机数写入到磁盘文件
4. 再从磁盘文件读取
5. 返回读取内容给响应。
<?php
//index.php
$s=""; //generate a random string of 108KB and a random filename
$fname = chr(rand(0,57)+65).chr(rand(0,57)+65).chr(rand(0,57)+65).chr(rand(0,57)+65).'.txt';
for($i=0;$i<108000;$i++)
{
$n=rand(0,57)+65;
$s = $s.chr($n);
}
//write s to a file
file_put_contents($fname,$s);
$result = file_get_contents($fname);
echo $result;
?>
下面是Node.js的代码:
//server.js
var http = require('http');
var server = http.createServer(handler);
function handler(request, response) {
//console.log('request received!');
response.writeHead(200, {'Content-Type': 'text/plain'});
s=""; //generate a random string of 108KB and a random filename
fname = String.fromCharCode(Math.floor(65 + (Math.random()*(122-65)) )) +
String.fromCharCode(Math.floor(65 + (Math.random()*(122-65)) )) +
String.fromCharCode(Math.floor(65 + (Math.random()*(122-65)) )) +
String.fromCharCode(Math.floor(65 + (Math.random()*(122-65)) )) + ".txt";
for(i=0;i<108000;i++)
{
n=Math.floor(65 + (Math.random()*(122-65)) );
s+=String.fromCharCode(n);
}
//write s to a file
var fs = require('fs');
fs.writeFile(fname, s, function(err, fd) {
if (err) throw err;
//console.log("The file was saved!");
//read back from the file
fs.readFile(fname, function (err, data) {
if (err) throw err;
result = data;
response.end(result);
});
}
);
}
server.listen(8124);
console.log('Server running at http://127.0.0.1:8124/');
测试结果:
#PHP:
Concurrency Level: 200
Time taken tests: 574.796 seconds
Complete requests: 2000
#node.js:
Concurrency Level: 200
Time taken tests: 41.887 seconds
Complete requests: 2000
我们都知道Node.JS一直很火也很强,其实很少人明白Node.JS到底强在哪里?
Node.JS在涉及堵塞源读写方面要比PHP快十多倍,从某种程度看,Node.JS与PHP相比有点不是一个级别,Javascript和PHP相比可能更合适,Node.JS是一个Web框架,其比PHP在涉及堵塞读写上要快,并不是Javascript语言比PHP快,而是Node.js处理堵塞源的方式比较高明。
所以,这不是简单语言细节之争,而是语言框架之争。将Node.JS这种异步机制从Javasctipt搬移到其他语言PHP .NET Java其实都会取得比原来堵塞方式有数十倍提高。
通常过去我们提升性能的思路是着重细节,比如Java提供Socket和文件等堵塞的NIO,那么是不是使用Java做一个Web应用,其性能肯定胜过Node.JS呢?未必。
再进一步,Java等提供线程的并发能力很强,而Node.JS只是单线程,涉及堵塞源的读写时,我们使用多线程并发编程是否在性能上就能胜过Node.JS呢?也未必,这其实是将并发能力用错了地方,堵塞源的读写本身是一种序列的串行操作。
Node.js强项不是在内部细节,而是在其高度,以及贴近Web应用这有的场景。Node.JS是一个平衡各个堵塞源的框架,而不是具体解决某个堵塞源的框架。
我曾经用红绿灯打比喻,一条大路上有多个红绿灯,如何保证这条大路上的车辆能更畅通通行呢?Node.JS的解决办法就是设法在车辆靠近每个红绿灯时都依次变成绿灯。而NIO或并发编程等解决办法则只是侧重红绿灯本身的改造提升。红绿灯路口类似各个堵塞源,比如Http Socket端口读写, 文件磁盘读写,数据库读写等等,我们很多时候侧重这些堵塞源内部性能微调,直至研究Linux的TCP协议,这是一种向内部向底层挖掘的思维方式。
Node.JS这种宏观异步事件处理机制,其核心是相当于一个事件队列,说白一点,相当于RabbitMQ这些消息系统或BUS等消息总线,我们已经体会过在大型分布式系统中消息中间件如何调度多个服务器之间的效率,同理,Node.JS将这种调度机制搬移到一台服务器内部,将服务器的Socket 文件读写等看成是一个堵塞式的独立服务器。
Node.JS的威力是其异步机制和事件驱动的架构设计魅力,现在流行的Reactive编程是将这种设计进行了推广。
来自Node.JS是皇帝新装一文对Node.JS产生的误解进行了分析,几个误解如下:
1.Node.js 是快的。
这分两点:
(1)在V8上运行JS很快?
JS其实比Java慢1到5倍,作者认为Java, Go, Erlang (HiPE), Clojure, C#, F#, Haskell (GHC), OCaml, Lisp (SBCL)等都很快,但是都不具生产性unproductive,没有一个适合编写Web服务器。
(2)Node.js是非堵塞,是并发的,基于事件的。
其实Node.JS是单线程,无关乎多线程并发,也不是唯一的能高效处理连接的框架,Vert.x, Erlang, Stackless Python, GHC, Go等也很强。
但是人们使用Node.JS能够以简单投篮的方式让他们网站提高很多性能。
作者最后认为,如果你来自一个过度设计的系统(意思指Spring等),做任何事情需要一个AbstractFactoryFactorySingletonBean时,而Node.JS正是因为其结构的缺乏让人耳目一新。
Node.JS让人们喜欢是因为其产品性productive,也就是开发的高效率,开发生产力很高:I’m so productive with Node.js! Agile! Fast! MVP!