注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

Minary_Acdream

http://f10.moe/

 
 
 

日志

 
 

Node.js学习-第二章-阻塞与非阻塞IO  

2014-06-07 21:15:46|  分类: node.js |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |
前言
    node.js为javascript引入了一个复杂的概念,在浏览器端从未有过:共享状态的并发
    通俗的讲,在node中,需要对回调函数如何修改当前内存中的变量(状态)特别小心。
    除此之外,还要也不注意对错误的处理是否会现在的修改这些状态,从而导致了整个过程的不可用。
    为了更好地理解这个概念,可以看下面这个例子:
          该函数在每次请求/books URL的时候都会被执行。
var books = ['A''B'];
function severBooks () {
    var html = '<b>' + books.join('</b></br><b>'+ '</b>';

    books = []; //特别注意这句,将books数组清空了。

    return html;
}

等价的PHP代码:
$books = array('A''B');
function serverBook(){
    $html = '<b>' . join($books, '</b></br><b>') . '</b>';
    $books = array();
    return $html;
}

            假设node服务器和PHP服务器同时发起两次对/books 的请求。结果如下:
            node服务器:第一次返回图书列表(A,B),第二次返回一个空的图书列表。
            PHP服务器: 两次都返回图书列表(A,B)。
            
            区别在于:
            node采取一个长期运行的进程。相反,Apache会产生多个线程(每个请求一个线程),每次都会刷新状态。
            在PHP中,当解释器再次执行时,变量$books会被重新赋值。
            而在node中,serverBook函数会再次被调用,且作用域中的变量不受影响(此时books数组仍为空)。
            此处区别需牢记,避免类似错误。)

阻塞
            尝试区分下面PHP代码和Node代码的区别:
//PHP 
printf('Hello');
sleep(5);
printf('World');
            
//node
console.log('Hello');
setTimeout(function(){
    console.log('World');
}, 5000);

           上述代码不仅仅是语义上的区别(node用了回调函数),两者区别集中展现在阻塞非阻塞上。
            PHP  sleep()阻塞了线程的执行,当程序进入睡眠的时候就什么事情都不做了。
            而Node使用了事件轮询,因此这里setTimeout是非阻塞的。也就是说,在setTimeout后面再加入console.log语句的话,该语句会被立刻执行。
//node
console.log('Hello');
setTimeout(function(){
    console.log('World');
}, 5000);

console.log('BYE');

//会被输出:
//Hello
//BYE
//World

          采用事件轮询意味着什么呢?
          从本质上讲,Node会先注册事件,随后不断地询问内核这些事件是不是已经分发。当事件分发时,对应的回调函数就会被触发,然后继续执行下去。
          如果没有事件发生,则继续执行其他代码,直到有新的事件,再去执行对应的回调函数。

          相反,在PHP中,sleep一旦被执行,执行会被阻塞一段指定的时间,并且在阻塞时间未到设定时间前,不会有任何操作,也就是说这是同步的。
          和阻塞相反,setTimeout只是注册了一个事件,而程序继续执行,所以这是异步的。
        
          Node的并发实现也采用了事件轮询。当Node接收到从浏览器发来的HTTP请求时,底层的TCP连接会分配一个文件描述符(文件描述符是抽象的句柄,存有对打开的文件,socket,管道等的引用
          随后,如果客户端向服务器发送数据,Node就会接受到给文件描述符上的通知,然后触发javascript的回调函数。

单线程
        Node是单线程的。但在Node.js 0.8后有了child_process模块。该模块提供了四个创建子进程的函数,分別是spawn,exec,execFile和fork。这些内容暂时先不了解,感兴趣的可以自己去查。

        总之,执行时只有一个进程,也就是说,当一个函数执行时,同一时间不会有第二个函数也在执行,那Node又是怎么做到高并发的呢?
        为了搞清楚这个问题,必须先理解堆栈的概念。
        
        当V8首次调用一个函数时,会创建一个众所周知的调用堆栈,或者称执行堆栈。
        调用堆栈最经常被用于存放子程序的返回地址。在调用任何子程序时,主程序都必须暂存子程序运行完毕后应该返回到的地址。因此,如果被调用的子程序还要调用其他的子程序,其自身的返回地址就必须存入调用堆栈,在其自身运行完毕后再行取回。在递归程序中,每一层次递归都必须在调用堆栈上增加一条地址,因此如果程序出现无限递归(或仅仅是过多的递归层次),调用堆栈就会产生堆栈溢出。---wiki

        V8执行javascript的速度非常快,非阻塞IO确保了单线程执行时,不会因为有数据库访问或者硬盘访问等操作而导致的挂起,因此说V8搭配非阻塞IO是最好的组合。
        

假设我们需要为某个请求相应数据库获取的数据:      
  http.createServer(function (req, res){   
     database.getInformaction(function (data){
        res.wirteHead(200);
        res.end(data);
    });
});

          可以看出,当请求到达时,调用堆栈中只有数据库调用。由于调用是非阻塞的,当数据库IO完成时,就完全取决于事件轮询合适再初始化新的调用堆栈。
          不过,在告诉Node“等你获取数据库响应时记得通知我”之后,Node就可以继续处理其他事情了。


错误处理
        首先,很重要的一点就是,Node应用依托在一个拥有大量共享状态的大进程中。如果,其中一个回调函数发生了错误,那么整个进程就都挂了。
        如果当前错误没有被捕获,那么这时候访问WEB服务器,进程就会崩溃。
        所以,每一步的错误处理都很关键,程序要很严谨。










  评论这张
 
阅读(67)| 评论(0)
推荐 转载

历史上的今天

在LOFTER的更多文章

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2018