深入浅出nodejs:第一章----node简介

1.node的特点

  作为后端jsvascript的运行平台,Node保留了前端浏览器对javascript中那些熟悉的接口,没有改写语言本身的任何特性,依旧基于作用域和原型链,区别在于它将前端中广泛运用的思想迁移到了服务器端.

1.1:异步I/O

如ajax请求:熟悉异步的用户必然知道,"收到响应"是在"发送ajax结束"之后输出的,在调用ajax方法后,后续代码是被立即执行的,而"收到响应"的执行时间是不被预期的,我们只知道它将在这个异步请求结束之后执行,但并不知道具体的时间点,异步调用中对于结果值的捕获是符合"dont call me,i will call you"原则的,这也是注重结果,不关心过程的一种表现.

在node中,异步也很常见,以读取文件为例,我们可以看到它与前端ajax调用极其类似:

  

 var fs = require('fs');
 fs.readFile('/path',function(err,file){
     console.log('读取文件完成');
 });
 console.log('发起读取文件');

这里的"发起读取文件"是在"读取文件完成"之前输出的.同样,"读取文件完成"的执行也取决于读取文件的异步调用何时结束.

在node中,绝大多数的操作都以异步的方式进行调用,ryan dahl排除万难,在底层构建了很多异步I/O的API,从文件读取到网路请求等,均是如此.这样的意义在于,在node中,我们可以从语言层面很自然的进行并行I/O操作.每个调用之前无需等待之前的I/O调用结束.在编程模型上可以极大提升效率.

下面的两个文件读取任务的耗时取决于最慢的那个文件读取的耗时:

fs.readFile('/path1',function(err,file){
     console.log('读取文件1完成');
 });
 fs.readFile('/path2',function(err,file){
     console.log('读取文件2完成');
 });

而对于同步I/O而言,他们的耗时是两个任务的耗时之和.这里异步带来的优势是显而易见的.

1.2:事件与回调函数

node将前端浏览器中应用广泛且成熟的事件引入后端,配合异步I/O,将事件点暴露给业务逻辑.

下面的例子展示的是ajax异步提交的服务器端处理过程.node创建一个web服务器,并侦听8080端口.对于服务器,我们绑定了request事件,对于请求对象,我们为其绑定了data事件和end事件:

    var http = require("http");
    var querystring = require("querystring");

    //侦听服务器的request事件
    http.craetServer(function(req,res){
        var postData = "";
        req.setEncoding("utf8");
        //侦听请求的data事件
        req.on("data",function(chunk){
            postData += chunk;
        });
        //侦听请求的end事件
        req.on("end",function(){
            res.end(postData);
        });
    }).listen(8080);
    console.log("服务器启动成功");

 相应的,我们在前端为ajax请求绑定了success事件,在发出请求之后,只需关心请求成功时执行的相应的业务逻辑即可,相关代码:

$.ajax({
    'url':'/url',
    'method':'POST',
    'data':{},
    'success':function(data){
         //success事件         
}
});

事件的编程方式具有轻量级,低耦合,只关注事务点灯优势,但是在多个异步任务的场景下,事件与事件之间个子独立,如何协作是一个问题.

从前面可以看到,回调函数无处不在,这是因为在javascript中,我们将函数作为第一等公民来对待,可以将函数作为对象传递给方法作为实参进行调用.

与其他的web后端编程语言相比,node除了异步和事件之外,回调函数是一大特色,纵观下来,回调函数也是最好的接受异步调用返回数据的方式.

 

1.3:单线程

Node保持了javascript在浏览器中单线程的特点.而且,javascript与其余线程是无法共享任何状态的.

单线程的最大好处是不用像多线程编程那样处处在意状态的同步问题,这里没有死锁的存在,也没有线程上下文交换所带来的性能上的开销.

单线程的弱点,这些弱点是学习node过程中必须面对的,积极面对这些弱点,可以享受到node带来的好处,也能避免潜在问题,使其得以高效利用.弱点主要有以下3方面:

  1.无法利用多核CPU

  2.错误会引起整个应用退出,应用的健壮性值得考验

  3.大量计算利用CPU导致无法继续调用异步I/O

像浏览器中javascript与UI共用一个线程一样,javascript长时间执行会导致UI的渲染和响应被中断,在Node中,长时间的CPU占用也会导致后续的异步I/O发不出调用,已经完成的异步I/O的回调函数也得不到及时执行.HTML5定制了web workers的标准,能够创建工作线程来进行计算,以解决javascript大计算阻塞UI渲染的问题,工作线程为了不阻塞主线程,通过消息传递的方式来传递运行结果,这也使得工作线程不能访问到主线程的UI.

 node采用了与web workers相同的思路来解决单线程中大计算量的问题:child_process.

 子进程的出现,意味着node可以从容地应对单线程在健壮性和无法利用多核CPU方面的问题.通过将计算分发到各个子进程,可以将大量计算分解掉,然后再通过进程之间的事件消息来传递结果,这可以很好地保持应用模型的简单和低依赖.通过Master-Worker的管理方式,也可以很好地管理各个工作进程,以达到更高的健壮性.

1.4:跨平台

起初,node只可以在Linux平台运行.在v0.6.0版本发布时,Node已经能够直接在window平台上运行了.

1.5:Node的应用场景

在进行技术选型之前,需要了解一项新技术具体什么样的场景,毕竟合适的技术用在合适的场景可以起到意想不到的效果.关于Node,探讨的较多的主要有I/O密集型和CPU密集型.

1.5.1:I/O密集型

在Node的推广过程中,无数次有人问起Node的应用场景是什么,如果将所有的脚本语言拿到一起来评判,那么从单线程的角度来说,Node处理I/O的能力是值得称赞的,通常,说Node擅长I/O密集型的场景应用基本是没有人反对的.node面向网络且擅长并行I/O,能够有效地组织起更多的硬件资源,从而更多好的服务.

I/O密集的优势主要在于Node利用事件循环的处理能力,而不是启动每一个线程为每一个请求服务,资源占用极少.

 1.5.2:是否不擅长CPU密集型业务

换一个角度,在CPU密集的应用场景中,Node是否能够胜任?实际上,V8的执行效率是非常,单以执行效率来做评判,V8的执行效率是毋庸置疑的.

CPU密集应型应用给Node带来的挑战主要是:由于javascript单线程的原因,如果有长时间运行的计算(比如大循环),将会导致CPU时间片不能释放,使得后续I/O无法发起.但是适当调整和分解大型运算任务为多个小任务,使得运算能够适时释放,不阻塞I/O的发起,这样既可以同时享受到并行异步I/O的好处,又能充分利用CPU.

关于CUP机密型应用,Node的异步I/O已经解决了再单线程上CUP与I/O之间阻塞无法重叠利用的问题,I/O阻塞造成的性能浪费远比CPU影响小.对于长时间运行的计算,如果它的耗时超过普通阻塞I/O的耗时,那么应用场景就需要重新评估,因为这类计算比阻塞I/O还影响效率,甚至说就是一个纯计算的场景,根本没有I/O.此类应用场景或许应当采用多线程的方式进行计算.

 

posted on 2015-07-14 10:37  谷子多多  阅读(200)  评论(0)    收藏  举报