学习服务器端的JavaScript_Node.js

时间:2021-11-05 22:00:54

翻译自: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环境还包括JaxerNarwhal。 然而,Node.js和这些解决方案不同的地方是,它基于事件模型,而不是线程模 型。例如,支持PHP和其它CGI脚本的web服务器Apache就是基于线程的,它为每一个传入的请求启动一个系统线程。虽然对于许多应用来说这没有问 题,但是许多需要长连接请求的应用,如FriendfeedGoogle 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.

  1. 从下载最新版本Node.js nodejs.org (撰写本文时的最新版本是0.1.31,翻译时是0.1.99)并解压缩它。
  2. 打开终端,运行下面的命令。
    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模块。

view plaincopy to clipboardprint?
  1. var sys = require("sys"),  
  2.     http = require("http");  
  3. http.createServer(function(request, response) {  
  4.     response.sendHeader(200, {"Content-Type""text/html"});  
  5.     response.write("Hello World!");  
  6.     response.close();  
  7. }).listen(8080);  
  8. sys.puts("Server running at http://localhost:8080/");  

此脚本导入syshttp模 块,并创建了一个HTTP服务器。传递给http.createServer的匿名函数,将在每请求到来时被调用。服务器被创 建之后,它会监听端口8080。当请求到来时,我们首先发送包含内容类型和状态代码200(表示成功)的HTTP协议头。然后,发送“Hello World!”并关闭连接。您可能会注意到,必须显式的关闭连接。发送流数据到客户端后忘记关闭连接是个很常见的错误。如果你运行这个脚本,到http://localhost:8080/在 浏览器中,你将看到“Hello World!”


第3步创建一个简单的静态文件服务器

好了,我们已经建立一个HTTP服务器,但是无论你访问任何网址,它除了发送“Hello World!”之外,不会做任何事情。所有的HTTP服务器必须能够发送静态文件,如HTML文件、图像或其他文件。如下代码实现这些功能:

view plaincopy to clipboardprint?
  1. var sys = rquire("sys"),  
  2.     http = require("http"),  
  3.     url = require("url"),  
  4.     path = require("path"),  
  5.     fs = require("fs");  
  6.   
  7. http.createServer(function(request, response) {  
  8.     var uri = url.parse(request.url).pathname;  
  9.     var filename = path.join(process.cwd(), uri);  
  10.     path.exists(filename, function(exists) {  
  11.         if(!exists) {  
  12.             response.sendHeader(404, {"Content-Type""text/plain"});  
  13.             response.write("404 Not Found\n");  
  14.             response.close();  
  15.             return;  
  16.         }  
  17.   
  18.         fs.readFile(filename, "binary"function(err, file) {  
  19.             if(err) {  
  20.                 response.sendHeader(500, {"Content-Type""text/plain"});  
  21.                 response.write(err + "\n");  
  22.                 response.close();  
  23.                 return;  
  24.             }  
  25.   
  26.             response.sendHeader(200);  
  27.             response.write(file, "binary");  
  28.             response.close();  
  29.         });  
  30.     });  
  31. }).listen(8080);  
  32.   
  33. sys.puts("Server running at http://localhost:8080/");  

首先加载所有需要的模块。包括syshttpurlpath, 以及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,在有消息时加载通知相应的监 听器。源代码的前几行都是用来导入需要的模块,并定义处理静态文件的函数,这和我们之前的例子相同。

view plaincopy to clipboardprint?
  1. var sys = require("sys"),  
  2.     http = require("http"),  
  3.     url = require("url"),  
  4.     path = require("path"),  
  5.     fs = require("fs"),  
  6.     events = require("events");  
  7.   
  8. function load_static_file(uri, response) {  
  9.     var filename = path.join(process.cwd(), uri);  
  10.     path.exists(filename, function(exists) {  
  11.         if(!exists) {  
  12.             response.sendHeader(404, {"Content-Type""text/plain"});  
  13.             response.write("404 Not Found\n");  
  14.             response.close();  
  15.             return;  
  16.         }  
  17.   
  18.         fs.readFile(filename, "binary"function(err, file) {  
  19.             if(err) {  
  20.                 response.sendHeader(500, {"Content-Type""text/plain"});  
  21.                 response.write(err + "\n");  
  22.                 response.close();  
  23.                 return;  
  24.             }  
  25.   
  26.             response.sendHeader(200);  
  27.             response.write(file, "binary");  
  28.             response.close();  
  29.         });  
  30.     });  
  31. }  

之前我们已经使用http模块创建了一个服务器,但它也可以使用这个模块创建一个HTTP客户端。我们将 创建一个HTTP客户端从Twitter的公共的timeline中加载tweets,主要功能在get_tweets函数中 实现。

view plaincopy to clipboardprint?
  1. var twitter_client = http.createClient(80, "api.twitter.com");  
  2.   
  3. var tweet_emitter = new events.EventEmitter();  
  4.   
  5. function get_tweets() {  
  6.     var request = twitter_client.request("GET""/1/statuses/public_timeline.json", {"host""api.twitter.com"});  
  7.   
  8.     request.addListener("response"function(response) {  
  9.         var body = "";  
  10.         response.addListener("data"function(data) {  
  11.             body += data;  
  12.         });  
  13.   
  14.         response.addListener("end"function() {  
  15.             var tweets = JSON.parse(body);  
  16.             if(tweets.length > 0) {  
  17.                 tweet_emitter.emit("tweets", tweets);  
  18.             }  
  19.         });  
  20.     });  
  21.   
  22.     request.close();  
  23. }  
  24.   
  25. setInterval(get_tweets, 5000);  

首先,我们创建一个访问api.twitter.com 80端口的HTTP客户端,并创建一个新的EventEmitterget_tweets函数向Twitter公开的timeline发出一个HTTP“GET”请求,并添加一个事件监听 器,这个监听器在Twitter服务器响应时被触发。由于Node.js是异步的,相应中的所有的数据,由”date”事件的监听器来处理。这个监听器只 是简单的追加信息到body变量。所有消息都传送完毕之后,监听”end”事件的监听器将被触发,然后解析传入的JSON数 据。如果服务器返回一条或多条tweets信息,我们触发tweet_emitter的”tweets”事件,并传递新的 tweets数据。这将触发所有监听”tweets”事件的监听器并将新的消息发送到到每个客户端。我们使用setInterval函 数,每5秒查询一次新消息 。

最后,需要创建HTTP服务器来处理请求。

view plaincopy to clipboardprint?
  1. http.createServer(function(request, response) {  
  2.     var uri = url.parse(request.url).pathname;  
  3.     if(uri === "/stream") {  
  4.   
  5.         var listener = tweet_emitter.addListener("tweets"function(tweets) {  
  6.             response.sendHeader(200, { "Content-Type" : "text/plain" });  
  7.             response.write(JSON.stringify(tweets));  
  8.             response.close();  
  9.   
  10.             clearTimeout(timeout);  
  11.         });  
  12.   
  13.         var timeout = setTimeout(function() {  
  14.             response.sendHeader(200, { "Content-Type" : "text/plain" });  
  15.             response.write(JSON.stringify([]));  
  16.             response.close();  
  17.   
  18.             tweet_emitter.removeListener(listener);  
  19.         }, 10000);  
  20.   
  21.     }  
  22.     else {  
  23.         load_static_file(uri, response);  
  24.     }  
  25. }).listen(8080);  
  26.   
  27. sys.puts("Server running at http://localhost:8080/");  

正如我们同我们的静态文件服务器一样,我们创建了一个监听8080端口的HTTP服务器。我们分析所请求的URL,如果URL等于"/stream" ,我们将处理它,否则由静态文件服务器处理。转发的功能由一个通过监听tweet_emitter接收新消息的监听器组 成,tweet_emmiter的事件由get_tweets函数触发。我们还将创建一个计时器,通过发送空数组的方式杀死超 过10秒的请求。当新消息到来时,我们以JSON的形式发送数据,并重置定时器。看过下面的客户端代码之后,你将对它的工作原理有更好的理解。将它保存为test.html, 并和服务器端JavaScript放置在同一目录下。

view plaincopy to clipboardprint?
  1. <!DOCTYPE html>  
  2. <html>  
  3.     <head>  
  4.         <title>Tweet Streamer</title>  
  5.         <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"></script>  
  6.     </head>  
  7.     <body>  
  8.         <ul id="tweets"></ul>  
  9.         <script type="text/javascript">  
  10.         var tweet_list = $("#tweets");  
  11.   
  12.         function load_tweets() {  
  13.             $.getJSON("/stream", function(tweets) {  
  14.                 $.each(tweets, function() {  
  15.                     $("<li>").html(this.text).prependTo(tweet_list);  
  16.                 });  
  17.                 load_tweets();  
  18.             });  
  19.         }  
  20.   
  21.         setTimeout(load_tweets, 1000);  
  22.         </script>  
  23.     </body>  
  24. </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!