这里有新鲜出炉的 Node.JS 入门教程,程序狗速度看过来!
Node.js 是一个基于 Chrome JavaScript 运行时建立的一个平台, 用来方便地搭建快速的 易于扩展的网络应用 · Node.js 借助事件驱动, 非阻塞 I/O 模型变得轻量和高效, 非常适合 运行在分布式设备 的 数据密集型 的实时应用
本文针对在 Node.js 关键的两个概念:非阻塞 IO 和事件循环进行了适当的总结, 需要的朋友可以参考下
学习和使用 Node.js 已经有两个月,使用 express 结合 mongoose 写了一个 web 应用和一套 RESTful web api,回过头来看 Node.js 官网首页对 Node.js 的介绍:Node.js uses an event-driven, non-blocking I/O model that makes it lightweight and efficient. 那么其中的 non-blocking I/O model 意味着什么呢?
非阻塞的 IO 模型
首先,IO 操作无疑是耗时的,当服务器端接收到大量请求时,为每一个请求创建进程或线程的同时,也增加了额外的内存开销,也可能浪费更多的时间资源。
由于 Node.js 是事件驱动的,于是它使用了事件循环来解决 IO 操作带来的瓶颈问题。在 Node.js 中,一个 IO 操作通常会带有一个回调函数,当 IO 操作完成并返回时,就会调用这个回调函数,而主线程则继续执行接下来的代码。简单的用一个例子来说明这个问题:
- request('http://www.google.com', function(error, response, body) {
- console.log(body);
- });
- console.log('Done!');
这段代码的意思是向'http://www.google.com'发出请求,当请求返回这则调用回调函数输出响应信息。由于 Node.js 的运行机制,这段代码运行后,会立即在控制台输出'Done!',然后一段时间后再输出响应的信息。
事件循环 event loop
接下来,来讨论下事件循环的机制。首先说说调用桟,比如有如下一段代码:
- function A(arg, func){
- var a = arg;
- func();
- console.log('A');
- }
- function B(){
- console.log('B');
- }
- A(0, B);
当代码执行后,函数 A 首先被推入调用桟中成为栈顶元素并开始执行 A,在执行过程中函数 B 又被推入调用桟成为栈顶元素,在 B 执行完成后,B 被弹出调用桟,A 再次成为栈顶元素,在 A 执行完成后 A 被弹出调用桟,调用桟呈空闲状态。
在 Javascript 运行时中存在一个消息队列,而消息和一个回调函数相关联,当一个事件被触发时,如果这个事件有相应的回调函数,则该消息就会被加入到消息队列中去。
回过头来说事件循环到底循环的是什么,在代码开始执行后,函数被不断推入调用桟中,就拿上面的例子来讲,request 被推入调用桟中,这个函数将进行一个 http 请求(这个 http 请求将交由 Node.js 的底层模块来实现)同时请求完成的事件和一个回调函数关联起来,request 被弹出调用桟,console.log 被推入调用桟开始执行。当请求完成时,完成事件被触发,一条消息被添加进消息队列中,消息队列首先会检查调用桟是否为空闲状态,如果调用桟并不空闲,则会一直等待到调用桟空闲状态后,将消息队列的头部弹出,此时与该消息相关联的回调函数被执行。
小结
以上就无阻塞模型和事件循环在概念上进行了总结。而这个事件循环的机制并不仅仅是 Node.js 所独有的,并且 Node.js 的代码是单线程执行的,在面对大量并发请求的时候,又有着什么优势呢?
上面这张图展示了 Node.js 的架构图,Node.js 的底层有一个模块负责维护线程池,当一个 IO 请求发出的时候,Node.js 的底层模块将新建一个线程来处理请求,完成后再将结果交还给上层。那么,当有多个请求的时候,Node.js 的底层模块将利用尽可能少的线程来完成最多的任务,如果存在空闲的线程,它将继续被利用来做其他的事情,这对于前面说的针对每个请求开一个新的进程或线程而言,无疑 "聪明" 许多,也更加高效了。
这篇文章是对学习 Node.js 的一个总结,其中若有问题和不足,欢迎批评指正。
来源: http://www.phperz.com/article/17/0425/274768.html