说到网页编程,大部分时间我用的都是ASP.NET或LAMP。现在,node.js成了新的亮点,在服务器端运行JavaScript代码的轻型平台,据说是通过使用异步I/O提升了性能。
该理论认为同步I/O阻塞模型是这样工作的:
I / O通常是一个网络事务中最重要的部分。当一个请求到达Apache Web服务器,它传递给PHP解释器来执行脚本的动态内容。现在到了棘手的部分,如果PHP脚本想要从磁盘/数据库读取或存取数据,这就成了最慢的一环。当你调用PHP函数 file_get_contents(),整个线程被阻塞,直到内容获取完成!在你的脚本获得文件内容之前,服务器不能做任何事情。想像一下当并发请求增加数倍时,会发生什么?他们要排队,因为没有可用的线程做这份工作,他们都被I/O阻塞了。
现在看一下node.js 独特的卖点。因为node.js在几乎所有的功能中都使用异步I / O,在上述方案中,只要文件检索功能(fs.readFile)被调用,服务器线程就会释放,。然后,一旦I / O完成,节点才会执行传进来的回调函数(早些时候通过fs.readFile)。在此期间,该有价值的线程就可以用于服务其它一些请求。
所以,这就是关于它的理论。但我不是那种只是因为它被大肆宣传或每个人都使用它就接受新风潮的人。不是的,我想了解事实真相,并亲自验证它,看看这个理论是否能经得起实际操作的考验。
所以我自己写了两个简单的脚本,一个用PHP(托管在apache2的),另一个是JavaScript(托管在node.js上的)。测试本身是非常简单的。该脚本将:
1,接受请求。 2,生成一个随机的108千字节的字符串。 3,将字符串写到磁盘的一个文件上。 4,从磁盘中读取内容。 5,在返回的响应流中返回字符串。
这是第一个脚本,index.php文件:
<?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;
这是第二个脚本:
//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/');
然后,然后我用apache评测工具,向他们两者一共发了2000个请求(200并发)。当我看到时间统计结果,大吃一惊:
#PHP:
Concurrency Level: 200
Time taken for tests: 574.796 seconds
Complete requests: 2000
#node.js:
Concurrency Level: 200
Time taken for tests: 41.887 seconds
Complete requests: 2000
真相大白,node.js比PHP快14倍!这样的结果是惊人的。它简直意味着node.js将在不久的未来成为编写性能驱动应用程序事实上的标准,毫无疑问!
大家一致认为的NodeJS生态系统还没有被广泛发掘,并且大多数node模块,比如数据库连接,网络接入,公用设施等,正在积极开发中。但尽管如此,看到这些结果,太显而易见了。在发展node.js应用程序所花费的任何额外的努力都是非常值得的。 PHP的可能仍然保持着“网页之王”的状态,但是作为焦点的node.js,我估计这种状态不会持续太久!
###更新
从下面看了一些评论之后,我觉得有义务也创建一个C#/mono 版本。不幸的是,这个已经被证明是最慢的一组(约40秒1个请求)。也许是mono任务库的执行很糟糕,或者我的代码有什么错误。一旦闲下来我就会解决它,并带来我的下一篇文章(可能就是ASP.NET vs node.js vs PHP!)
###第二次更新
至于C#/ASP.NET,是我可以管理的最佳版本。但它仍然滞后于php和node.js(我已经在Linux/Mono和Window-Sever-2012/IIS环境下进行了测试)。也许ASP.NET本来就更慢,所以之后我会更改这个参考标准,来做一个比较:
public class Handler : System.Web.IHttpHandler
{
private StringBuilder payload = null;
private async void processAsync()
{
var r = new Random ();
//generate a random string of 108kb
payload=new StringBuilder();
for (var i = 0; i < 54000; i++)
payload.Append( (char)(r.Next(65,90)));
//create a unique file
var fname = "";
do{fname = @"c:\source\csharp\asyncdemo\" + r.Next (1, 99999999).ToString () + ".txt";
} while(File.Exists(fname));
//write the string to disk in async manner
using(FileStream fs = File.Open(fname,FileMode.CreateNew,FileAccess.ReadWrite))
{
var bytes=(new System.Text.ASCIIEncoding ()).GetBytes (payload.ToString());
await fs.WriteAsync (bytes,0,bytes.Length);
fs.Close ();
}
//read the string back from disk in async manner
payload = new StringBuilder ();
StreamReader sr = new StreamReader (fname);
payload.Append(await sr.ReadToEndAsync ());
sr.Close ();
//File.Delete (fname); //remove the file
}
public void ProcessRequest (HttpContext context)
{
Task task = new Task(processAsync);
task.Start ();
task.Wait ();
//write the string back on the response stream
context.Response.ContentType = "text/plain";
context.Response.Write (payload.ToString());
}
public bool IsReusable
{
get {
return false;
}
}
}
参考文献:
1 https://en.wikipedia.org/wiki/Node.js
2 http://notes.ericjiang.com/posts/751
4.https://code.google.com/p/node-js-vs-apache-php-benchmark/wiki/Tests
From:http://www.prahladyeri.com/2014/06/php-vs-node-js-real-statistics/