翻译自:http://net.tutsplus.com/tutorials/javascript-ajax/learning-serverside-javascript-with-node-js/ 转载请声明原文链接http://www.grati.org/?p=181
Node.js是一个热门框架,使用它可以非常容易的创建高性能,实时网络应用程序。Node使JavaScript可以编写服务器端应用。本教程将引导你安装node并编写第一个“Hello World”程序,之后并扩展为一个实时的Twitter服务器。
什么是Node.js?
传统意义上JavaScript只运行在Web浏览器中,但是由于CommonJS 项目,人们对于将其运行在服务器端产生了浓厚的兴趣。其他服务器端JavaScript环境还包括Jaxer和Narwhal。 然而,Node.js和这些解决方案不同的地方是,它基于事件模型,而不是线程模 型。例如,支持PHP和其它CGI脚本的web服务器Apache就是基于线程的,它为每一个传入的请求启动一个系统线程。虽然对于许多应用来说这没有问 题,但是许多需要长连接请求的应用,如Friendfeed或Google Wave,线程模型并没有很好的扩展性。
Node.js使用事件循环而非线程,这样他能够扩展到数百万的并发连接。它克服如下缺陷,服务器花费其大部分时间用于等待I/O操作,如从硬盘 读取一个文件,访问一个外部Web服务或等待一个文件上传完毕,因为这些操作对比内存操作来说是非常慢的。每个I/O操作在Node.js都是异步的,也 就是说,在I/O操作正在进行的同时,服务器可以继续处理传入的请求。JavaScript非常适合基于事件的编程,因为它的匿名函数和闭包特性使内联回 调函数的编写变得异常简单,并且JavaScript开发人员早已熟悉这样的技巧。此事件为基础的模式,使得Node.js速度非凡。并能轻松满足实时应 用程序的需求。
第1步安装
Node.js运行在基于Unix的系统上,如Mac OS X,Linux和FreeBSD。不幸的是,目前暂不支持Windows操作系统(翻译这篇文章时已经通过cygwin支持windows了)。因此,如 果您是Windows用户,您可以使用VirtualBox在Ubuntu Linux中安装使用。具体操作,请参考教程 。您需要使用终端来安装和运行Node.js.
- 从下载最新版本Node.js nodejs.org (撰写本文时的最新版本是0.1.31,翻译时是0.1.99)并解压缩它。
- 打开终端,运行下面的命令。
cd /path/to/nodejsmake
sudo make install编译安装Node.js时,会在终端输出大量的信息。
第2步Hello World!
每一个新技术都以“Hello World!”教程开始,所以我们将创建一个简单的HTTP服务器,输出这个消息。但是,首先你要了解Node.js的模块系统。在node中,功能被封 装在模块中,要使用这些功能必须首先加载相应的模块。Node.js文档中 列出了很多模块。可以使用函数require
加载模块,如下所示:
var sys = require("sys");
如上代码加载sys模块,其中包含处理系统级任务的功能,如输出到终端。要使用一个模块中的一个函数,您必须通过存储模块的变量来调用它,在我们 的例子中sys
就是这个变量。
sys.puts("Hello World!");
使用node
后跟JavaScript文件名就可执行这两条简单的命令。
node test.js
执行上面的命令将在终端上输出“Hello World!”。
要创建一个HTTP服务器,你必须加载
http
模块。
- var sys = require("sys"),
- http = require("http");
- http.createServer(function(request, response) {
- response.sendHeader(200, {"Content-Type": "text/html"});
- response.write("Hello World!");
- response.close();
- }).listen(8080);
- sys.puts("Server running at http://localhost:8080/");
此脚本导入sys
和http
模 块,并创建了一个HTTP服务器。传递给http.createServer
的匿名函数,将在每请求到来时被调用。服务器被创 建之后,它会监听端口8080。当请求到来时,我们首先发送包含内容类型和状态代码200(表示成功)的HTTP协议头。然后,发送“Hello World!”并关闭连接。您可能会注意到,必须显式的关闭连接。发送流数据到客户端后忘记关闭连接是个很常见的错误。如果你运行这个脚本,到http://localhost:8080/
在 浏览器中,你将看到“Hello World!”
第3步创建一个简单的静态文件服务器
好了,我们已经建立一个HTTP服务器,但是无论你访问任何网址,它除了发送“Hello World!”之外,不会做任何事情。所有的HTTP服务器必须能够发送静态文件,如HTML文件、图像或其他文件。如下代码实现这些功能:
- var sys = rquire("sys"),
- http = require("http"),
- url = require("url"),
- path = require("path"),
- fs = require("fs");
- http.createServer(function(request, response) {
- var uri = url.parse(request.url).pathname;
- var filename = path.join(process.cwd(), uri);
- path.exists(filename, function(exists) {
- if(!exists) {
- response.sendHeader(404, {"Content-Type": "text/plain"});
- response.write("404 Not Found\n");
- response.close();
- return;
- }
- fs.readFile(filename, "binary", function(err, file) {
- if(err) {
- response.sendHeader(500, {"Content-Type": "text/plain"});
- response.write(err + "\n");
- response.close();
- return;
- }
- response.sendHeader(200);
- response.write(file, "binary");
- response.close();
- });
- });
- }).listen(8080);
- sys.puts("Server running at http://localhost:8080/");
首先加载所有需要的模块。包括sys
,http
,url
,path
, 以及fs
或filesystem模块。接下来,如之前那样我们创建一个HTTP服务器。这一次,我们将使用url
模 块解析传入的URL请求,找出要访问文件的路径。通过使用path.join
,或根据当前工作目录生成请求文件在服务器硬盘上的的实际路径。下一步,我们检查该是否文件存在,它是一个异步操作,因此需要一个回调。如果该文件不存 在,返回404错误。否则,我们使用fs
模块以“二进制”方式读取文件,然后发送到用户。如果读取文件出错中,我们将错误信息 传递给用户,并关闭连接。因为这一切都是异步的,所以无论从磁盘读取多大的文件,服务器都能够继续响应其他请求。
如果你运行这个例子,并 访问http://localhost:8080/path/to/file
,该文件将在您的浏览器中显示。
第4步实时的Tweet转发服务器
在静态文件服务器的基础上,下一步我们将使用Node.js建立一个转发Tweet数据到客户端的服务器。要完成这个程序,我们必须使用另一个额外 的模块:events
。Node有一个EventEmitter
机制,用于处理异步任务的事件监听 器。就像在jQuery或其他客户端JavaScript框架中一样,你可以将监听器绑定到鼠标点击事件或AJAX请求上,Node允许你将许多东西绑定 到事件监器,其中一些我们已经使用。可以监听的事件包括每一个I/O操作,例如读写文件,检查文件是否存在,等待HTTP请求,等等。EventEmitter
简 化绑定和解除保定的步骤,并负责触发相应的事件侦听器。我们将使用一个EventEmitter
,在有消息时加载通知相应的监 听器。源代码的前几行都是用来导入需要的模块,并定义处理静态文件的函数,这和我们之前的例子相同。
- var sys = require("sys"),
- http = require("http"),
- url = require("url"),
- path = require("path"),
- fs = require("fs"),
- events = require("events");
- function load_static_file(uri, response) {
- var filename = path.join(process.cwd(), uri);
- path.exists(filename, function(exists) {
- if(!exists) {
- response.sendHeader(404, {"Content-Type": "text/plain"});
- response.write("404 Not Found\n");
- response.close();
- return;
- }
- fs.readFile(filename, "binary", function(err, file) {
- if(err) {
- response.sendHeader(500, {"Content-Type": "text/plain"});
- response.write(err + "\n");
- response.close();
- return;
- }
- response.sendHeader(200);
- response.write(file, "binary");
- response.close();
- });
- });
- }
之前我们已经使用http
模块创建了一个服务器,但它也可以使用这个模块创建一个HTTP客户端。我们将 创建一个HTTP客户端从Twitter的公共的timeline中加载tweets,主要功能在get_tweets
函数中 实现。
- var twitter_client = http.createClient(80, "api.twitter.com");
- var tweet_emitter = new events.EventEmitter();
- function get_tweets() {
- var request = twitter_client.request("GET", "/1/statuses/public_timeline.json", {"host": "api.twitter.com"});
- request.addListener("response", function(response) {
- var body = "";
- response.addListener("data", function(data) {
- body += data;
- });
- response.addListener("end", function() {
- var tweets = JSON.parse(body);
- if(tweets.length > 0) {
- tweet_emitter.emit("tweets", tweets);
- }
- });
- });
- request.close();
- }
- setInterval(get_tweets, 5000);
首先,我们创建一个访问api.twitter.com 80端口的HTTP客户端,并创建一个新的EventEmitter
。get_tweets
函数向Twitter公开的timeline发出一个HTTP“GET”请求,并添加一个事件监听 器,这个监听器在Twitter服务器响应时被触发。由于Node.js是异步的,相应中的所有的数据,由”date”事件的监听器来处理。这个监听器只 是简单的追加信息到body
变量。所有消息都传送完毕之后,监听”end”事件的监听器将被触发,然后解析传入的JSON数 据。如果服务器返回一条或多条tweets信息,我们触发
tweet_emitter的”tweets”事件,并传递新的 tweets数据。这将触发所有监听”tweets”事件的监听器并将新的消息发送到到每个客户端。我们使用setInterval
函 数,每5秒查询一次新消息 。
最后,需要创建HTTP服务器来处理请求。
- http.createServer(function(request, response) {
- var uri = url.parse(request.url).pathname;
- if(uri === "/stream") {
- var listener = tweet_emitter.addListener("tweets", function(tweets) {
- response.sendHeader(200, { "Content-Type" : "text/plain" });
- response.write(JSON.stringify(tweets));
- response.close();
- clearTimeout(timeout);
- });
- var timeout = setTimeout(function() {
- response.sendHeader(200, { "Content-Type" : "text/plain" });
- response.write(JSON.stringify([]));
- response.close();
- tweet_emitter.removeListener(listener);
- }, 10000);
- }
- else {
- load_static_file(uri, response);
- }
- }).listen(8080);
- sys.puts("Server running at http://localhost:8080/");
正如我们同我们的静态文件服务器一样,我们创建了一个监听8080端口的HTTP服务器。我们分析所请求的URL,如果URL等于"/stream"
,我们将处理它,否则由静态文件服务器处理。转发的功能由一个通过监听tweet_emitter
接收新消息的监听器组 成,tweet_emmiter的事件由get_tweets
函数触发。我们还将创建一个计时器,通过发送空数组的方式杀死超 过10秒的请求。当新消息到来时,我们以JSON的形式发送数据,并重置定时器。看过下面的客户端代码之后,你将对它的工作原理有更好的理解。将它保存为test.html
, 并和服务器端JavaScript放置在同一目录下。
- <!DOCTYPE html>
- <html>
- <head>
- <title>Tweet Streamer</title>
- <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"></script>
- </head>
- <body>
- <ul id="tweets"></ul>
- <script type="text/javascript">
- var tweet_list = $("#tweets");
- function load_tweets() {
- $.getJSON("/stream", function(tweets) {
- $.each(tweets, function() {
- $("<li>").html(this.text).prependTo(tweet_list);
- });
- load_tweets();
- });
- }
- setTimeout(load_tweets, 1000);
- </script>
- </body>
- </html>
我们编写一个简单的HTML页面,使用jQuery库,并定义一个无序列表保存消息。客户端JavaScript缓存消息列表,并在一秒钟后运行load_tweets
函 数。这使浏览器有足够的时间在开始AJAX请求之前完成页面的载页。load_tweets
函数非常简单:它使用jQuery 的getJSON
加载/stream
。当服务器返回数据后,我们遍历所有的消息和并加入消息列表中。然后,再次调用load_tweets
。这样就高效的创建了一 个加载新消息的循环,这个循环将在10秒钟后超时。客户端和服务器之间保持长连接,所有新消息被实时推送到客户端。这种技术被称为长轮询。
接 下来你可以使用node
执行服务并访问http://localhost:8080/test.html
,你会看到Twitter公共timeline上的消息实时发送到到您的浏览器中。
接下来的步骤
Node.js是一个非常令人兴奋的技术,可以轻松地创建高性能的实时应用。我希望你能在自己的应用中使用它的先进特性。node设计良好的模块系 统,可以使它很容易地与你的现有代码相结合。而且还有几乎包罗一切的第三方模块 – 包括数据库连接层,模板引擎,邮件客户端,甚至连所有这些的完整框架。你可以在Node.js wiki上看到完整的模块列表 ,你可以在How To Node找到更多的教程 。我推荐你去JSConf看一段node的发明者瑞安达尔讲述node设计理念的视频。这里是连接 。
我希望你喜欢这个教程。如果您有任何意见,你 可以在这里留言或发的Twitter 消息给我。Happy noding!