Chinaunix首页 | 论坛 | 博客
  • 博客访问: 2441700
  • 博文数量: 328
  • 博客积分: 4302
  • 博客等级: 上校
  • 技术积分: 5486
  • 用 户 组: 普通用户
  • 注册时间: 2010-07-01 11:14
个人简介

悲剧,绝对的悲剧,悲剧中的悲剧。

文章分类

全部博文(328)

文章存档

2017年(6)

2016年(18)

2015年(28)

2014年(73)

2013年(62)

2012年(58)

2011年(55)

2010年(28)

分类: Web开发

2013-12-18 22:45:03

异步编程

如果你了解异步(Asynchronous)编程的话,将会非常简单。 这里的A和Ajax里的”A"是同一个单词。在node.js中每一个方法都是异步的。因此事实上任何一个“阻断”线程的东西都运行在后台。这是大家需要务必了解的。例如,如果你尝试阅读一个文件,你需要指定一个Callback方法来控制阅读操作完成后的动作。

你需要自己处理全部

Node.js提供的是一个环境,这个环境中你需要自己处理所有事情。 所以呢,这里没有缺省的web服务器,或者类似服务器。对于新手来说可能有点恐怖,但是回报也是巨大的,你将得到超棒性能的web应用。一个脚本来处理所有客户端的通信。这很大程度的减少了应用消耗的资源。

交互式shell

  1. $ node
  2. > console.log('Hello World');
  3. Hello World
  4. undefined

模块

node.js使用了模块化的架构体系来简化复杂应用。模块类似于C的类库和pascal中的unit。每一个模块包含了相关主题的一套功能。例如,你可以使用http模块来处理HTTP相关的功能。同时node.js提供了一些核心的类库帮助你访问文件系统中的文件,创建HTTP和TCP/UDP服务器,及其其它相关的实用功能。包含一个模块非常简单:

var http = require('http');

require()方法返回指定模块的参考。在这个代码中,一个HTTP模块的参考将保存在http变量中。

在以上代码中,我们传递了模块的名字到require()方法。这是的Node搜索node_module目录来查询http模块,如果没有找到模块,将会到全局模块缓存中查询。你可以指定一个实际的文件,如下:

var myModule = require('./myModule.js');

模块包含了代码片段。模块中的代码大部分是私有的 ,这意味你不能在外部直接访问变量。只能在模块内部访问。当然,你可以使用expose来将变量或者方法倒出模块,这样你可以在模块外部使用,如下:
  1. var PI = Math.PI;
  2.  
  3. exports.area = function (r) {
  4.   return PI * r * r;
  5. };
  6.  
  7. exports.circumference = function (r) {
  8.   return 2 * PI * r;
  9. };
这个代码中,PI是一个私有的模块变量。下面两个方法将给予exports对象创建。这些方法能够在模块外部被访问因为它们基于exports方法。因此,你可以保证area()和circumference()将会按照它们原本的行为来实现操作。

全局变量

node.js基于google的v8 javascript引擎,所以我们需要遵守客户端的最佳实践。例如,我们需要避免将任何内容知道全局变量中。当然,不是任何情况都可以做到的。Node中使用的全局范围是GLOBAL,你可以通过忽略var关键字来定义一个全局变量:

安装新模块

Node.js拥有一个包管理器,叫做Node Package Manager(NPM)。自动随node.js安装,你可以使用NPM来安装模块。执行如下语句:

npm install module_name
上面这个命令会把模块安装到当前项目(目录)下,如果要全局安装的话要加 -g 参数。

如果你的程序包含很多“依赖”(Dependency),那再利用该方法安装它们就不合适了。为此npm提供了一个package.json文件。
  1. {
  2.   "name" : "MyStaticServer",
  3.   "version" : "0.0.1",
  4.   "dependencies" : {
  5.     "express" : "3.3.x"
  6.   }
  7. }
package.json文件包含了应用程序的基本信息。其中“dependencies”部分描述了你想安装模块的名称和版本。该案例,接受Express 3.3的任何版本。你可以在该部分列出你想要的所有依赖。
代替之前一个个安装每个依赖,现在我们可以运行一个命令,即可将它们全部安装完成。

$ npm install  
运行该命令,npm将在当下文件夹中查找“package.json”文件。一旦找到,即可安装所列出的所有依赖。

如果你是需要一个公司代理才能访问网络的话,就需要进行一些特殊的配置才能让npm可以访问到网络上的资源,以下三种方法任选其一
  1. npm --https-proxy=http://proxy.company.com:8080 -g install karma

  2. or

  3. npm config set proxy http://proxy.company.com:8080
  4. npm config set https-proxy http://proxy.company.com:8080

  5. or

  6. export npm_config_proxy http://proxy.company.com:8080
  7. export npm_config_https_proxy http://proxy.company.com:8080

文件的输入与输出

通过内置的文件(fs)模块,我们可以很容易进行文件的输入/输出操作。fs模块有一个readFile方法,该方法以文件路径、回调函数为参数。该回调函数在完成文件读取后调用。文件数据读取后存储在Buffer类型中,为基本的字节数组。我们可以通过toString()方法将它转化为字符串。

fs.readFile()方法接受3个参数:文件名称,编码(可选),callback方法。
fs.writeFile()方法接受文件名称和数据作为参数。同时接受第三个和第四个参数(都是可选的)来指定编码和callback方法。

  1. // Load the fs (filesystem) module
  2. var fs = require('fs');
  3.   
  4. // Read the contents of the file into memory.
  5. fs.readFile('example_log.txt', function (err, logData) {
  6.   
  7.   // If an error occurred, throwing it will
  8.   // display the exception and end our app.
  9.   if (err) throw err;
  10.   
  11.   // logData is a Buffer, convert to string.
  12.   var text = logData.toString();
  13. });

异步回调

正如在上例中看到的那样,Node.js典型的模式是使用异步回调。基本上,你告诉Node.js要做的事,它执行完后便会调用你的函数(回调函数)。这是因为Node是单线程的。在你等待回调函数执行过程中,Node可继续执行其他事务,不必被阻塞直到该请求完毕。

这对于Web服务器尤其重要。在现代Web应用访问数据库的过程中特别普遍。当你等待数据库返回结果的过程中,Node可以处理更多请求。与每次连接仅处理一个线程相比,它使你以很小的开销来处理成千上万个并行连接。

HTTP服务器的例子

Node内建有一个模块,利用它可以很容易创建基本的HTTP服务器。你运行该代码,并在浏览器中输入“”,你将看见该文本。
  1. // Include http module.
  2. var http = require("http");
  3.  
  4. // Create the server. Function passed as parameter is called on every request made.
  5. // request variable holds all request parameters
  6. // response variable allows you to do anything with response sent to the client.
  7. http.createServer(function (request, response) {
  8.    // Attach listener on end event.
  9.    // This event is called when client sent all data and is waiting for response.
  10.    request.on("end", function () {
  11.       // Write headers to the response.
  12.       // 200 is HTTP status code (this one means success)
  13.       // Second parameter holds header fields in object
  14.       // We are sending plain text, so Content-Type should be text/plain
  15.       response.writeHead(200, {
  16.          'Content-Type': 'text/html'
  17.       });
  18.       // Send data and end response.
  19.       response.end('Welcome to GBin1.com!');
  20.    });
  21. // Listen on the 8080 port.
  22. }).listen(8080);

处理URL参数

我们需要自己处理Node.js中的任何东西,包括处理参数:
  1. // Include http module,
  2. var http = require("http"),
  3. // And url module, which is very helpful in parsing request parameters.
  4.    url = require("url");
  5.  
  6. // Create the server.
  7. http.createServer(function (request, response) {
  8.    // Attach listener on end event.
  9.    request.on('end', function () {
  10.       // Parse the request for arguments and store them in _get variable.
  11.       // This function parses the url from request and returns object representation.
  12.       var _get = url.parse(request.url, true).query;
  13.       // Write headers to the response.
  14.       response.writeHead(200, {
  15.          'Content-Type': 'text/plain'
  16.       });
  17.       // Send data and end response.
  18.       response.end('Here is your data: ' + _get['data']);
  19.    });
  20. // Listen on the 8080 port.
  21. }).listen(8070);
这个代码使用url模块的parse()方法,来将请求的URL转换为一个对象。返回的对象拥有一个query属性,可以用来取得URL参数。保存这段代码为get.js并且执行:
node get.js
打开浏览器并且输入地址/?data=put_gbstring_here。你将看到效果。

更复杂的文件输入输出例子

代码展示如何使用fs.readFile()和fs.writeFile()。每一次服务器收到一个请求,脚本就会自动添加1,然后写回文件。

  1. // Include http module// Include http module,
    var http = require("http"),
    // And mysql module you've just installed.
       fs = require("fs");
     
    // Create the http server.
    http.createServer(function (request, response) {
       // Attach listener on end event.
       request.on("end", function () {
          // Read the file.
          fs.readFile("test.txt", 'utf-8', function (error, data) {
             // Write headers.
             response.writeHead(200, {
                'Content-Type': 'text/plain'
             });
             // Increment the number obtained from file.
             data = parseInt(data) + 1;
             // Write incremented number to file.
             fs.writeFile('test.txt', data);
             // End response with some nice message.
             response.end('This page was refreshed ' + data + ' times!');
          });
       });
    // Listen on the 8080 port.
    }).listen(8080);
结果好像有bug,这里每次加了2。其实这不是一个错误。每一次你请求这个URL的时候,俩个请求被发送到服务器。第一个请求是浏览器自动请求的 favion.ico。当然第二个请求是我们脚本对应URL()。所以要对请求进行一个判断,下面是修改后的代码。


  1. // Include http module,
  2. var http = require("http"),
  3. // And mysql module you've just installed.
  4.    fs = require("fs");
  5.  
  6. // Create the http server.
  7. http.createServer(function (request, response) {
  8.    // Attach listener on end event.
  9.    request.on('end', function () {
  10.       // Check if user requests /
  11.       if (request.url == '/') {
  12.          // Read the file.
  13.          fs.readFile('test.txt', 'utf-8', function (error, data) {
  14.             // Write headers.
  15.             response.writeHead(200, {
  16.                'Content-Type': 'text/plain'
  17.             });
  18.             // Increment the number obtained from file.
  19.             data = parseInt(data) + 1;
  20.             // Write incremented number to file.
  21.             fs.writeFile('test.txt', data);
  22.             // End response with some nice message.
  23.             response.end('This page was refreshed ' + data + '
  24.          });
  25.       } else {
  26.          // Indicate that requested file was not found.
  27.          response.writeHead(404);
  28.          // And end request without sending any data.
  29.          response.end();
  30.       }
  31.    });
  32. // Listen on the 8080 port.
  33. }).listen(8080);

执行系统命令

  1. //
  2. var sys = require('sys')
  3. var exec = require('child_process').exec;
  4. var child;

  5. // executes `pwd`
  6. child = exec("pwd", function (error, stdout, stderr) {
  7.   sys.print('stdout: ' + stdout);
  8.   sys.print('stderr: ' + stderr);
  9.   if (error !== null) {
  10.     console.log('exec error: ' + error);
  11.   }
  12. });
或者
  1. var sys = require('sys')
  2. var exec = require('child_process').exec;
  3. function puts(error, stdout, stderr) { sys.puts(stdout) }
  4. exec("ls -la", puts);

访问Mysql数据库 

对于一个正常的服务器端技术,肯定需要有机制来处理数据库操作。为了在node.js中使用数据库,我们需要安装类库,这里我们使用node-mysql。完整的名称是mysql@2.0.0-alpha2(@后面是版本号)。打开你的控制台,导航到你保存脚本的目录,执行如下命令:

npm install mysql@2.0.0-alpha2
这将下载和安装模块,同时创建node_module目录到目前目录中。下面我们看看如何使用代码访问mysql:
  1. // Include http module,
  2. var http = require('http'),
  3. // And mysql module you've just installed.
  4.    mysql = require("mysql");
  5.      
  6. // Create the connection.
  7. // Data is default to new mysql installation and should be changed according to your configuration.
  8. var connection = mysql.createConnection({
  9.    user: "root",
  10.    password: "",
  11.    database: "db_name"
  12. });
  13.  
  14. // Create the http server.
  15. http.createServer(function (request, response) {
  16.    // Attach listener on end event.
  17.    request.on('end', function () {
  18.       // Query the database.
  19.       connection.query('SELECT * FROM your_table;', function (error, rows, fields) {
  20.          response.writeHead(200, {
  21.             'Content-Type': 'x-application/json'
  22.          });
  23.          // Send data as JSON string.
  24.          // Rows variable holds the result of the query.
  25.          response.end(JSON.stringify(rows));
  26.       });
  27.    });
  28. // Listen on the 8080 port.
  29. }).listen(8080);
查询数据库非常简单,输入查询语句和callback方法。在一个实际的应用中,你应该检查是否有错误!(一旦有错误,error参数将不会为undefined),并且发送响应代码基于查询成功或者失败,注意我们设定content-type为x-application/json,这是正确的JSON MIME类型。rows参数包含了查询结果,这里我们使用JSON,stringify()方法简单的将rows中的数据转换为JSON结构

保存脚本为mysql.js,执行:

node mysql.js
在浏览器中输入,你可以看到要求你下载JSON格式数据文件。

调试

Node.js 支持命令行下的单步调试。在命令行下执行 node debug debug.js,将会启动调试工具
命令 功能
run 执行脚本,在第一行暂停
restart 重新执行脚本
cont,c 继续执行,直到遇到下一个断点
next,n 单步执行
step,s 单步执行并进入函数
out,o 从函数中步出
setBreakpoint(),sb() 在当前行设置断点
setBreakpoint(‘f()’),sb(...) 在函数f的第一行设置断点
setBreakpoint(‘script.js’,20),sb(...) 在script.js的第20行设置断点
clearBreakpoint,cb(...) 清除所有断点
backtrace,bt 显示当前的调用栈
list(5) 显示当前执行到的前后5行代码
watch(expr) 把表达式expr加入监视列表
unwatch(expr) 把表达式expr从监视列表移除
watchers 显示监视列表中所有的表达式和值
repl 在当前上下文打开即时求值环境
kill 终止当前执行的脚本
scripts 显示当前已加载的所有脚本
version 显示V8的版本
另外还有基于 eclipse 和 node-inspector 的调试方式,这里就不一一介绍了。

自动重启服务

如果你有 PHP 开发经验,会习惯在修改 PHP 脚本后直接刷新浏览器以观察结果,而你在开发 Node.js 实现的 HTTP 应用时会发现,无论你修改了代码的哪一部份,都必须终止Node.js 再重新运行才会奏效。这是因为 Node.js 只有在第一次引用到某部份时才会去解析脚本文件,以后都会直接访问内存,避免重复载入,而 PHP 则总是重新读取并解析脚本(如果没有专门的优化配置)。Node.js的这种设计虽然有利于提高性能,却不利于开发调试,因为我们在开发过程中总是希望修改后立即看到效果,而不是每次都要终止进程并重启。
supervisor 可以帮助你实现这个功能,它会监视你对代码的改动,并自动重启 Node.js。
使用方法很简单,首先使用 npm 安装 supervisor:
  1. $ npm install -g supervisor

  2. $ supervisor app.js
  3. DEBUG: Running node-supervisor with
  4. DEBUG: program 'app.js'
  5. DEBUG: --watch '.'
  6. DEBUG: --extensions 'node|js'
  7. DEBUG: --exec 'node'
  8. DEBUG: Starting child process with 'node app.js'
  9. DEBUG: Watching directory '/home/byvoid/.' for changes.
  10. HTTP server is listening at port 3000.
  11. 当代码被改动时,运行的脚本会被终止,然后重新启动。在终端中显示的结果如下:
  12. DEBUG: crashing child
  13. DEBUG: Starting child process with 'node app.js'
  14. HTTP server is listening at port 3000.
同时,也可以使用forever
forever start -a /yourcodepath/app.js

一些有用的资料

初学者教程:
英文API: 
中文API: 
Express:   
某应用:  
https://github.com/nswbmw/N-blog
阅读(2041) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~