node.js基础

node.js 语法
node.js基础
文章图片

最近一段时间在用node.js做一个web项目(JMarketPlace),以前的印象是javascript是一种前端语言,但是在接触到backbone和node.js后,彻底更新了我对javascript的观念,它真的很强大,在这一项目中,从前端到后端都是用的javascript,对于我不得不说这是一次挑战。在开始这个项目之前没有接触过node.js,在项目中需要用到什么就是查,虽然走了一些弯路但是还是在磕磕碰碰中走过来了,这里我想对node.js进行总结。官方给出了如下定义:

Node.js is a platform built onChrome's JavaScript runtimeforeasily building fast, scalable network applications. Node.js uses anevent-driven, non-blocking I/O model that makes it lightweight and efficient,perfect for data-intensive real-time applications that run across distributed devices.
  • 异步式I/O与事件驱动
Node.js最大的特点就是采用异步式I/O与事件驱动的架构设计。对于高并发的解决方案,传统的架构是多线程模型,也就是为每个业务逻辑提供一个系统线程,通过系统线程切换来弥补同步式I/O调用时的时间开销。Node.js 使用的是单线程模型,对于所有I/O 都采用异步式的请求方式,避免了频繁的上下文切换。Node.js 在执行的过程中会维护一个事件队列,程序在执行时进入事件循环等待下一个事件到来,每个异步式I/O 请求完成后会被推送到事件队列,等待程序进程进行处理。

例如,对于简单而常见的数据库查询操作,按照传统方式实现的代码如下:
res= db.query('SELECT * from some_table'); res.output();

以上代码在执行到第一行的时候,线程会阻塞,等待数据库返回查询结果,然后再继续处理。然而,由于数据库查询可能涉及磁盘读写和网络通信,其延时可能相当大(长达几个到几百毫秒,相比CPU的时钟差了好几个数量级),线程会在这里阻塞等待结果返回。对于高并发的访问,一方面线程长期阻塞等待,另一方面为了应付新请求而不断增加线程,因此会浪费大量系统资源,同时线程的增多也会占用大量的CPU 时间来处理内存上下文切换,而且还容易遭受低速连接攻击。看看Node.js是如何解决这个问题的:

db.query('SELECT* from some_table', function(res){ res.output(); });

这段代码中db.query 的第二个参数是一个函数,我们称为回调函数。进程在执行到db.query的时候,不会等待结果返回,而是直接继续执行后面的语句,直到进入事件循环。当数据库查询结果返回时,会将事件发送到事件队列,等到线程进入事件循环以后,才会调用之前的回调函数继续执行后面的逻辑。

  • 回调函数
之前使用javascript并不多,所以对回调函数这个机制不是很熟悉,这里将对回调函数进行总结。让我们来想像一个基本场景,我们要干某件耗费大量时间的事,比如试图读取一个大文件,我们不想让Node.js服务必须读完这个文件才能响应别的请求,为了完成这样的功能,我们最好是告诉Node.js在后台读取这个文件,当读取完之后通过某种方式通知我们,而在这个过程中,服务器同时还能处理别的请求,也就是使我们的代码变成“非阻塞”,就像我们刚刚举的例子一样,以适用于同时时间处理多个请求的情况,我们也把这个叫做异步调用。
下面是一个非阻塞文件读取器的代码:

var http = require('http'); var fileSystem = require('fs'); http.createServer(function (request, response) { response.writeHead(200, {'Content-Type': 'text/plain'}); var read_stream = fileSystem.createReadStream('myfile.txt'); read_stream.on('data', writeCallback); read_stream.on('close', closeCallback); function writeCallback(data){ response.write(data); }function closeCallback(){ response.end(); }}).listen(8080); console.log('Server started');

这里我们使用了Node.js内置的文件系统模块,用.createReadStream()读取我们的文件,并绑定两个函到"data"和"close"事件。这些函数将在事件引发时执行。把上述代码存为"fileReader.js",在同级目录下新建一个文本文件"myfile.txt",执行"node fileReader.js"命令,现在你可以在http://localhost:8080 看到浏览器里显示了myfile.txt内容了。
  • 调用本地exe应用
Node.js提供了调用本地程序的api(http://nodejs.org/api/child_process.html),这里简单介绍一下exec方法:
child_process.exec(command,[options], callback)
下面是一个小示例:

var exec = require('child_process').exec, child; child = exec('cat *.js bad_file | wc -l', function (error, stdout, stderr) { console.log('stdout: ' + stdout); console.log('stderr: ' + stderr); if (error !== null) { console.log('exec error: ' + error); } });

在回调函数中处理出错信息或者打印正确信息。
node.js web开发
【node.js基础】
  • Express框架+Jade引擎模版

npm提供了大量的第三方模块,其中不乏许多Web 框架,我们没有必要重复发明轮子,因而选择使用 Express作为开发框架,因为它是目前最稳定、使用最广泛,而且Node.js 官方推荐的唯一一个 Web开发框架。Express( http://expressjs.com/ ) 除了为 http 模块提供了更高层的接口外,还实现了许多功能,其中包括:

  • 路由控制;
  • 模板解析支持;
  • 动态视图;
  • 用户会话;
  • CSRF 保护;
  • 静态文件服务;
  • 错误控制器;
  • 访问日志;
  • 缓存;
  • 插件支持。
varexpress = require('express'); varapp = express.createServer(); app.use(express.bodyParser()); app.all('/', function(req,res) { res.send(req.body.title+ req.body.text); }); app.listen(3000);

可以看到,我们不需要手动编写 req 的事件监听器了,只需加载 express.bodyParser()就能直接通过 req.body 获取 POST 的数据了。


  • 工程的结构
我们回过头来看看Express都生成了哪些文件。除了 package.json,它只产生了两个JavaScript文件app.js和routes/index.js。模板引擎 ejs 也有两个文件 index.ejs和layout.ejs,此外还有样式表 style.css。下面来详细看看这几个文件。App.js是工程的入口Routes/index.js是路由文件,相当于控制器,用于组织展示的内容
exports.index = function(req, res) { res.render('index', {title: 'Express' }); };

app.js中通过 app.get('/',routes.index); 将“/ ”路径映射到 exports.index函数下。其中只有一个语句 res.render('index', {title: 'Express' }),功能是调用模板解析引擎,翻译名为 index 的模板,并传入一个对象作为参数,这个对象只有一个属性,即 title: 'Express'。PS:title属性是一定要定义的,不然会出错。
Views/index.jade和views/layout.jade是模板文件

  • 工作原理
当通过浏览器访问 app.js 建立的服务器时,会看到一个简单的页面,实际上它已经完成了许多透明的工作,现在就让我们来解释一下它的工作机制,以帮助理解网站的整体架构。访问http://localhost:3000,浏览器会向服务器发送以下请求:

GET / HTTP/1.1 Host: localhost:3000 Connection: keep-alive Cache-Control: max-age=0 User-Agent: Mozilla/5.0 AppleWebKit/535.19 (KHTML, like Gecko)Chrome/18.0.1025.142 Safari/535.19 Accept:text/html,application/xhtml+xml,application/xml; q=0.9,*/*; q=0.8 Accept-Encoding: gzip,deflate,sdch Accept-Language: zh; q=0.8,en-US; q=0.6,en; q=0.4 Accept-Charset: UTF-8,*; q=0.5

其中第一行是请求的方法、路径和HTTP 协议版本,后面若干行是 HTTP 请求头。app 会解析请求的路径,调用相应的逻辑。app.js 中有一行内容是 app.get('/',routes.index),它的作用是规定路径为“/”的 GET 请求由routes.index 函数处理。routes.index通过res.render('index', { title:'Express' }) 调用视图模板 index,传递 title变量。最终视图模板生成HTML 页面,返回给浏览器,返回的内容是:

HTTP/1.1 200 OK X-Powered-By: Express Content-Type: text/html; charset=utf-8 Content-Length: 202 Connection: keep-alive Express - 锐客网 ExpressWelcome to Express

浏览器在接收到内容以后,经过分析发现要获取/stylesheets/style.css,因此会再次向服务器发起请求。app.js 中并没有一个路由规则指派到 /stylesheets/style.css,但 app 通过app.use(express.static(__dirname + '/public')) 配置了静态文件服务器,因此/stylesheets/style.css 会定向到 app.js所在目录的子目录中的文件public/stylesheets/style.css,向客户端返回以下信息:

HTTP/1.1 200 OK X-Powered-By: Express Date: Mon, 02 Apr 201215:56:55 GMT Cache-Control: public,max-age=0 Last-Modified: Mon, 12Mar 2012 12:49:50 GMT ETag: "110-1331556590000" Content-Type: text/css; charset=UTF-8 Accept-Ranges: bytes Content-Length: 110 Connection: keep-alive body { padding: 50px; font: 14px "LucidaGrande", Helvetica, Arial, sans-serif; } a { color: #00B7FF; }

由 Express 创建的网站架构如下图所示。

这是一个典型的MVC 架构,浏览器发起请求,由路由控制器接受,根据不同的路径定向到不同的控制器。控制器处理用户的具体请求,可能会访问数据库中的对象,即模型部分。控制器还要访问模板引擎,生成视图的HTML,最后再由控制器返回给浏览器,完成一次请求。
node.js plugin(with C++)

Node.js的许多插件是用C/C++来写的,其官方api中给出了addon的相关说明,http://nodejs.org/api/addons.html,虽然项目最终没有用上这一方面的东西,但是我觉得这块的知识还是挺重要的,它涉及到node.js和C/C++的集成。
需要特别强调的是,在window下用node-gyp来build相关C++代码的时候,前期环境的配置很重要,在这一块我走了很多弯路,最终导致我的工作机不能安装Microsoft Visual Studio C++ 2010,最后不得不用自己的电脑来测试。在window下,相关的配置工作如下:


参考文献:

http://nodejs.org/api/
http://www.laonan.net/blog/64/
node.js开发指南
转载于:https://www.cnblogs.com/williamxiao/archive/2013/04/21/3499961.html

    推荐阅读