找回密码
 用户注册

QQ登录

只需一步,快速开始

查看: 3834|回复: 0

[译]什么是Node.js

[复制链接]
发表于 2011-12-27 13:47:37 | 显示全部楼层 |阅读模式

Node.js被认为是一种很新潮的编程语言,有着和RailsAjax和Hadoop一样的传统,甚至在某种程度上和iPhone编程()和HTML5一样新潮。到一个大的技术会议上,你一定能发现一些与Node.js相关的演讲。

如果进入到更深的层面,你会了解到Node.js(被大多数人简称为Node)是一个服务器端的Javascript解决方案,尤其长于处理响应HTTP请求。当人们聊到如何用Node处理端口、套接字、线程,你即使不会感到惊奇,也会有点晕,这真的是Javascript吗?世界上会有人在浏览器外运Javascript吗,更不用说是在浏览器上运行了?

你没有听错,你还会进一步了解到Node是聚焦于网络编程和服务端请求/回应处理的。但与此同时,和Rails、Ajax和Hadoop一样,关于Node的可用信息很少。当然,随着时间推移,会有越来越多关于Node.js的信息,但是为什么要等到一本书或者一本使用手册出现以后才开始用Node呢?现在你就可以开始使用Node提升你的代码的可维护性、减轻编程压力。

<!--more-->

给Node专家的建议

Node和大多数新出现的只被少数人所知道的技术一样:它古怪难用,只有很少的人才能驾驭。因此,如果你从未用过Node,你就要开始从一些最基本的服务端脚本开始了。但是,你要先花时间搞清楚到底是怎么回事,Node尽管是一种Javascript,但它和你熟悉的客户端的Javascript运行起来并不一样。实际上,你需要扭转你已经习惯于Javascript的思维方式。

如果你已经转变了你的思维方式了,那就意味着你已经使用Node一到两年了,这篇文章的大部分内容对你来说就太过简单。你也许会找一些在客户端使用Node的例子或者是关于事件IO处理和反应模型(reactor patterns)的讨论。这些东西虽然有趣,并且将Node推进到一个很高的地位,但它们对于初学者来说太难。因此,你可以将这篇文章转给你的还不知道Node的同事,当他们意识到Node的有用性时,再带他们看一些更为高级的Node的使用例子。

Node:一些基本的例子

先讲关键的:你需要明白Node是用来运行单独的Javascript程序的,而并非由HTML加载到浏览器中运行的。它存在于本地文件系统中,由Node程序(类似于守护进程,监听着一个特定端口)执行。

跳过hello world

在Node网站上有一个经典的例子“Hello World”。几乎每个人都是从Hello World开始学习的,所以你可以自己去网上看一下这个例子,然后开始下面更为有趣的例子:一个可以发送静态文件的服务端,而不仅仅是一行文字:

  1. var sys = require("sys"),
  2.            http = require("http"),
  3.            url = require("url"),
  4.            path = require("path"),
  5.            fs = require("fs");
  6.        http.createServer(function(request, response) {
  7.            var uri = url.parse(request.url).pathname;
  8.            var filename = path.join(process.cwd(), uri);
  9.            path.exists(filename, function(exists) {
  10.                if(!exists) {
  11.                    response.writeHead(404, {"Content-Type":
  12. "text/plain"});
  13.                    response.end("404 Not Found\n");
  14.                    return;
  15.                }
  16.                fs.readFile(filename, "binary", function(err, file)
  17. {
  18.                    if(err) {
  19.                        response.writeHead(500, {"Content-Type":
  20. "text/plain"});
  21.                        response.end(err + "\n");
  22.                        return;
  23.                    }
  24.                    response.writeHead(200);
  25.                    response.end(file, "binary");
  26.                });
  27.            });
  28.        }).listen(8080);
  29.        console.log("Server running at http://localhost:8080/");
复制代码

非常感谢Mike Amundsen提供的一些相似代码。这一段例子是Devon Govett在Nettuts+培训博客上贴出来的,在某些地方做了一些修改。如果你已经掌握了一些基本部分,Devon的完整的指导性的例子可以帮助你学得更快。

如果你是新学Node,将这段代码敲到一个文本文件中病保存为NodeFileServer.js,然后到Node的网站上去下载Node或者去工具库()中查找是否已经安装了。你需要从源文件开始编译这段代码,如果你对Unix不太熟悉,不了解其编译和配置,那么可以通过网络上的指示得到帮助。

Node运行Javascript,但并不是Javascript

别担心你已经将NodeFileServer.js放了一会儿了,你很快就会回来的,并且写更多代码。现在,享受一下程序实现的乐趣吧,刚才,你已经通过Unix配置编译了程序:

./configure

make

make install

这回让你意识到另外一件事情:Node本身不是Javascript,Node是一个用来运行Javascript的程序。事实上,Node是一个C程序。如果你去Node的源文件夹Node/src下查看的你会看到如下内容:

所有认为Javascript写Server端工具很弱的人都错了。是的,Javascript不是用来处理操作系统层的套接字以及网络连接的,但是Node不是用Javascript写的,而是用C写的,而C语言很擅长处理网络联接中涉及到的繁重的工作。Javascript很容易向C程序发送指令,这些指令就能在操作系统层执行了。事实上,对大多数程序员来说,Javascript比C语言容易学——这一点在现在看起来可能没什么,但是却是大家愿意认真去看待Node的理由。

Node的基本用法进一步反映了尽管Node和Javascript一起工作,但它本身并不是Javascript。你可以从命令行中开始运行如下:

— (bdm0509@Bretts-MacBook-Pro Sun, 29 May 11) —  —  —  —  —  —  —  —  —  — (/Users/bdm0509/tmp/Node/src) —

— (09:09 $)-> export PATH=$HOME/local/Node/bin:$PATH

— (bdm0509@Bretts-MacBook-Pro Sun, 29 May 11) —  —  —  —  —  —  —  —  —  — (/Users/bdm0509/tmp/Node/src) —

— (09:09 $)-> cd ~/examples

— (bdm0509@Bretts-MacBook-Pro Sun, 29 May 11) —  —  —  —  —  —  —  —  —  —  —  — (/Users/bdm0509/examples) —

— (09:09 $)-> Node NodeFileServer.js

Server running at http://127.0.0.1:1337/

你就可以看到程序运行结果了。尽管对上面的运行状态还有很多需要解释的地方,对于端口1337所发生的事情也需要进一步解释,但现在你已经可以借此明白Node是一个需要你输入Javascript的程序。Node接下来对Javascript所做的可能并不值得耗费笔墨:在某种程度上,仅仅是Javascript要做什么,它就做什么。这使你可以放心大胆去写Javascript,而不必担心要去学C。Node的一大吸引人的地方就是你可以不用学C而写出一个Server端。

和一个“Node Sever”交互

确保你写的NodeFileServer.js还在Node上运行,然后你就可以通过你的机器上的1337端口看到输出了。

是的,这就是你能得到的东西了,平凡无奇。然而,当你意识到你只用了大约20行代码就写了一个文件服务器端时,你就不会觉得它平凡了。你所看到的输出——你所写的脚本的实际代码——并不是包含在脚本自身里面。它是在文件系统中运行服务的。在相同目录中放入一幅图像,然后在URL后面加上图像名字,比如http://localhost:8080/my_image.png:

Node就能很容易处理这幅二进制图像了。当你再一次看到代码有多简洁时,你会觉得这实在是了不起的。在这个的基础上,用Javascript写出你自己的Server代码能有多难?不仅如此,假设你想写能处理多个请求的代码?(这只是个提示,比如打开4个、5个或者10个浏览器,然后访问服务器)Node的优美之处就在于你可以用简单的平淡无奇的Javascript来完成上述功能。

对上述代码的详细解释

相比一个在Server上跑的实际代码,还有很多需要讨论的地方。在进行下一步之前,来快速看看NodeFileServer.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/');

首先,你对require()函数进行了一个调用。require()函数是很多程序员呼唤已久的。你可以在Javascript模块的讨论中看到这一点,也可以在CommonJS看到,以及O'Reilly 作者David Flanagan在 2009做的一个很漂亮的实现。换句话说,require()对你来说也许很新鲜,但它绝不是未经测试的随意的Node代码。它是应用模块Javascript的核心,Node大量应用了这个函数。

接下来,所产生的http变量被用来创建服务器。当服务器被连上以后,会接到一段功能代码来执行。这段特殊的功能代码完全忽略请求并且只是写一个输出,仅仅是一段文本,说:“Hello World\n”。

但这段代码显示了Node用法的基本框架:

  • 定义交互类型,得到一个在这个交互中工作的变量(通过require())。
  • 创建一个新的服务器(通过createServer())。
  • 交给这个服务器一个函数用来处理请求,请求处理函数需要包含一个请求以及一个回复。
  • 让服务器在一个特定端口和IP上处理请求(通过listen)。

翻译中存在的问题

尽管你可以用Javascript很轻易地写出Server代码了(不管实际代码是用C运行还是其他语言运行的),但下面的问题仍然存在:你应该用Javascript来写一个Server吗?要回答这个问题,考虑下面的一个典型应用例子。

一个典型的JSON往返

你已经有一个很典型的Web应用了,一个有着CSS前端风格的HTML,以及用来和server进行交互的Javascript。又因为你是在一个交互网络中,你使用Ajax,也不依赖表单的POST功能从服务端获得数据。如果你平时就是这么做的,那么你用起JSON(http://www.json.org/)来,也会觉得很舒服,因为这它几乎是现在通过网络发送数据的最常见方式了。

假设你接到了一个Ajax请求,内容为“在一个拍卖网站上给出一个特定吉他的更多信息”。这个请求通过网络传播到在另外一个地方的Server上运行的PHP程序中。这个PHP服务器需要发回很多信息给这个Javascript请求方,并且发送回的信息要以一种特定形式打包以便Javascript可以将包打开。所以,返回的信息可以组织到一个阵列中,并转化为JSON,像下面这样:

$itemGuitar = array(

  'id' => 'itemGuitar',

  'description' => 'Pete Townshend once played this guitar while his own axe ' .

                    was in the shop having bits of drumkit removed from it.',

  'price' => 5695.99,

  'urls' => array('http://www.thewho.com', 'http://en.wikipedia.com/wiki/Pete_Townshend')

);

$output = json_encode($itemGuitar);

print($output);

回到客户端,Javascript客户端收到了这些信息,它们已经因为JSON和传输过程而发生了一些细微变化。客户端得到的内容基本如下所示:

{

  "id": "itemGuitar",

  "description": "Pete Townshend once played this guitar...",

  "price": 5695.99,

  "urls": ["http://www.thewho.com", "http://en.wikipedia.com/wiki/Pete_Townshend"]

}

这是一种很标准的表达。接下来,就很容易将这些文本内容转化为Javascript中的对象了。你只需要调用eval(),像下面这样:

var itemDetails = eval('(' + jsonDataString + ')');

结果是一个很漂亮的Javascript对象,这个对象可以很容易和JSON的矩阵结构匹配。当然,由于jsonDataString通常是由Server返回的,你可能更喜欢下面的代码:

var itemDetails = eval('(' + request.responseText + ')');

这是一个典型的JSON交互过程,但存在非常大的问题。

细节之处破坏代码

首先,最大的问题是这种代码对解释器translator的依赖性非常大。在上面的应用例子中,解释器是JSON以及相关代码的翻译者,存在两种依赖:eval()形式的Java对JSON解释器的依赖;PHP对JSON解释器的依赖。例如在PHP5.2.0中,解释器是包含在PHP中的,但它是作为外部支持出现的,独立于PHP的核心代码。

其次,是关于翻译本身的问题。没有任何东西告诉你会有下面的问题存在,比如将“I”翻译成“i”,或者将数组中的第一个元组处理成了第二个元组。在JSON正式发布之前,对它进行了很多测试,测试表明,上面的问题确实是存在的。

Javascript和PHP对工具包的依赖(还有其他的语言如C 、Lisp 、 Clojure 、 Eiffel等,看看下面各种语言的工具包就知道了)的确是一个很大的问题。换句话说,问题不在于翻译,而在于翻译器。尽管编程语言发展较慢,但这些语言的应用却发展很快。这导致了JSON被应用在非常复杂的领域或者在几个月前还无人接触的领域。每经过一次这样的过程,都会产生数据类型的新的递归和组合,而这些可能是翻译器不能翻译的。

这本身是不坏的,但是,这让人们对JSON如此流行以至一次有一次被投入新的使用的情况产生了怀疑。每次有新的应用出现,人们都在问,“JSON支持这个新的应用吗?”因此JSON一次有一次改版,这意味着不断的测试以及大量发布的新平台。作为程序员,要么重新组织你的数据,要么等待一个能满足你要求的新版本,或者你自己对JSON进行改写。而这些,都被算到了所谓编程开销里面。

设想一下你可以跳过翻译部分——因此能跳过翻译器,设想你可以直接写端对端的Javascript而不是JSON的往返过程,将会怎样?

Node就可以让你做到这些。刚刚你所读到的PHP在JSON中的应用,比如数字变成对象,数据以新的方式配置,都不需要再考虑了。你只需要用Javascript发送数据、接收数据、回复数据就可以了。

eval()在JavaScript中是(潜在的)的罪魁祸首

上面还没有足够的理由来展现Node的安全性,对于eval()通过接受一行字符串来运行,人们一直认为这是很危险的。它运行的代码在你看来只是一些文本数据,就跟SQL从一个不经验证的文本框中接受SQL语言一样,容易带来SQL注入攻击以及其他恶意攻击,eval()也可能遭受这样的为危险。当它接收一行字符串来执行的时候,有可能就开启了恶意代码的大门。人们都对此小心翼翼,在网络上可以看到大量相关文章,只要Google一下"eval JavaScript evil"或者 "eval JavaScript injection" 就可以了。

虽然Node没有使用任何文本,但这并不意味着你可以不使用eval(),因此还是会有潜在威胁。然而,Node能让你避免eval()带来的问题,因为它是事件型Javascript或者事件型I/O,就是“事件型”解决了问题。要搞清楚“事件型”到底是什么含义,搞清楚它是如何让你避免了eval()的威胁,你不仅需要了解在应用中JSON往返的方式,还要了解应用在Web上是如何组织的。

如今的网络是一个事件型网络

很多网络表单都是事件提交者,换句话说,很多数据输入和选择都在发生——用户填写文本框,选择选项,选择表单等等——所有这些信息都被提交到服务器。从编程角度来看,这只是一个单独的数据提交的事件,通常是通过POST完成的。在Ajax之前,网络大多数都是这样操作的。

一次性发送很多数据

通过Ajax,事件型编程(evented programming)可以做更多。有更多激起和服务器交互的事件,经典的例子就是输入一个邮编,然后就可以从服务器得到一个城市名和国家名。由于有Ajax 和 XmlHttpRequest,大量数据就不必一次性全部发到服务器端。然而,这并没有改变Web是一个事件驱动的地方。Ajax被大量用来构建有趣的图景、快速验证、提交表单,却没有被用来构建事件型网络。所以尽管表单没有用POST提交大量数据,Ajax却提交了很多数据。

事实上,这是稍微缺了点儿创意的Ajax程序员造成的。每次你发出一个请求——不管这个请求有多小——都会有大量网络通信。服务器需要对这个请求进行应答,而这个应答是通过在线程中创建一个新的进程做到的。所以如果你真的有一个事件型网络,可以让10~15个微请求达到服务器端,那么你就会在服务器端看到10~15个线程。现在将这个数据乘以1000,10000或者1000000个页面……这就会造成混乱,带来网络性能下降、系统崩溃。

因此,网络需要是一个最小化的或者中等程度的事件地点。这样的整合让服务器端的程序不是发回很小的回复给很小的请求,而是发送大量数据,这就要用到JSON,从而带来了eval()的问题。问题就出在eval(),但是这个问题同时也是HTTP网络通信固有的问题。

有些Javascript的高级程序员可能对这一点大不以为然,他们太熟悉eval()的使用了,知道它不会带来这样的问题。但事实上,这些程序员们使用的是类似于JSON.parse()的东西,而不是eval()。也有很多文章说明了需要谨慎使用eval(),说明这一点确实是需要注意的。再去看看在类似于Stack Overflow这样的网站上有多少关于eval()的问题,你就会意识到很多人使用eval()的方式并不正确,很多程序员都没有意识到eval()存在的问题。

发送很少的数据

Node带来了不同的解决方式:它让你和你的Web应用进入到一个事件型模型。换句话说,它不是用很多数据来发送很少的请求,而是用很少的数据发送很多请求,或者这些请求所得到的回复数据量也较少。在某些情形下,你需要回忆一下你的GUI编程。(所有使用Java Swing的程序员终于可以使用他们的GUI知识了。)一个用户输入了他们的名字和姓,当他们还在移向下一个框时,一个验证名字有效性(是否重名等)的请求已经发出去了。对于邮政编码、地址、电话号码也是如此。对于页面上的每一个可以想见的动作,都有这样的持续的请求和应答发生。

那么,用Node会带来什么不一样的地方呢?前面说的关于线程的问题不存在了吗?在Node的网站上,他们做了如下回答:

“Node的目标是提供一个简易的方式来构建一个可拓展的网络程序。在‘hello world’服务器的例子中……很多客户连接可以并行处理。Node告诉操作系统每当有一个新的连接建立时都要通知它(通过epoll, kqueue, /dev/poll或者select),然后它就进入睡眠状态。如果有人发起新的连接,就会执行唤醒操作。每一次连接都只是一个很小的堆分配操作。”

Node中没有块,没有线程需要竞争相同资源(Node让事情按照它们希望发生的方式发生)。Node只是在等待(实际上是没有用到的Node回应者在休息),当请求到来时,它就进行处理。这就使得编程很快,不需要写大量服务端代码。

是的,确实会有混乱

值得指出的是这种模型会带来所有没有块的系统都会带来的问题:如果一个进程(而非线程)在写数据的同时另外一个数据来读数据,就会读到“脏数据”等。但你需要认识到在Web表单上的事件型编程的大部分都是只读的。在一个微请求中,很少会去修改数据。会有不断的验证操作、数据查找操作,但很少有写操作。在这种情况下,只管发送请求就可以了。数据库自己会去加锁,而不需要服务器去加锁,而且数据库操作通常比服务器要快。

此外,Node确实有计划支持进程派生,HTML5 Web开发者API也支持这个功能实现。当你使用事件型编程的时候,你可能很少会使用到这个功能。但是,你要关心的只是你的Web应用如何,你该发送和接收多少数据,而不是Node如何工作。

在正确的地点正确的时间

这里已经有另外一种Web模型了,这可能比你用不用Node或者你的Web应用是如何用事件驱动的更重要。问题不过是针对不同的问题使用不同的解决方式。更好的方式是,用对的方法去解决特定的问题,而不管这种方法是否是你以前用来解决问题的方法。

熟悉带来的惰性

不仅是在Web设计中,在所有的编程中都有一种惰性。这种惰性可以描述如下:你对某种方法或者技术或者语言学得越多掌握得越好,你就越能广泛使用这种方法或者技术或者语言。这是一个听起来很有道理的准则,但仔细想想却不是这样。充分掌握一门语言或者一个工具并广泛应用它是很好的,但这种惰性却会让你陷入以下情景:你使用一个工具只是因为你知道它,而不是因为它是正确的工具。

现在来看Ajax,前面已经讨论过一点了。最初,Ajax为向服务器发送一个不带表单的快速请求提供了一个很好的解决方法。现在,现在,它却用在所有的表单提交中。这就是滥用一种技术。现在有很多的Web应用提交表单都是使用Ajax,仅仅因为顶尖的Web开发者依赖于Ajax。

同样,你可能会为Node感到欣喜——因为你已经接受了你刚才在文章中读到的和看到的——并在很多地方使用Node。一时间,你将你所有的PHP或者Perl写的后端都换成了Node。结果会是怎样呢?一团糟。事实上,你会被迫使用好几个Web表单做原本Node不应该做的事情。

而那些事情并非是Node擅长做的。Node最擅长做的是处理微请求和事件型I/O。使用Node在网页和服务器件进行快速交流。使用表单提交发送大量数据给服务器。使用PHP和Perl处理数据库提升和产生动态网页。使用Node为服务器端的Javascript提供一种处理小的请求的方式。使用Rail和Spring以及servlets或者任何你想要用的。但要保证你选择的工具是最适合的,而不是你恰好知道的。

Node对简洁性的承诺

关于Node,还有一件事值得一提。当你上述方式来编程时,你会经常发现你不必深入每一个工具包或者API或者你使用的框架。只需要使用最适用的工具就可以了。使用适用的工具意味着你会使用到工具的核心能力,这样就会造就通才——知道很多事情的程序员——你也会减少对专家的需要——他们只是对一种或者两者工具知道得非常非常好。当然,每一个精明的经理都会发现这样的专家是很难找的。

学习Node可能需要你付出一点努力,但却是很值得的一件事情。为什么?因为你解决Web应用问题时就只需要用到Javascript了。这意味着现在的Javascript专家也可以来写相关程序。当你需要使用PHP或者Perl时——因为他们是解决某个问题的正确的工具——你不需要详尽了解它们了,你只需要知道最基本的部分即可。灵活性在你解决新的问题时体现出来,而不是勉强使用一个很差的解决方式。


你最大的挑战是大家全部涌向Web,它被谈论得越来越多,被分得越来越细,技术越来越庞杂。然而,掌握100门技术的核心永远比只掌握一门技术而试图用这一门技术解决100个问题好。Node以及事件型I/O确实不是包治百病,但它确实能解决一些很重要的问题。
原文链接:What is Node.js?
译文来源:http://www.webapptrend.com/
WebAppTrend是一个独立的技术博客,关注Web App前瞻和实践,以及智能浏览器发展

请大家在关注CSDN的同时,关注我们的新浪微博 @WebAppTrend,欢迎加入我们的QQ群:193775364



作者:ydj9931 发表于2011-12-26 23:03:45 原文链
您需要登录后才可以回帖 登录 | 用户注册

本版积分规则

Archiver|手机版|小黑屋|ACE Developer ( 京ICP备06055248号 )

GMT+8, 2024-11-21 23:09 , Processed in 0.018844 second(s), 5 queries , Redis On.

Powered by Discuz! X3.5

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表