【Node.js】如何理解非阻塞I/O(详解)

Node.js

想要学好 node ,必须要理解 node 的特性。
node 有三大特性:单线程、非阻塞I/O、事件驱动
如果你已经深刻理解了这三大特性,会发现这三大特性其实说的是一个事,三者少一不可,共同组成了这个神奇的 node。


非阻塞 I/O

非阻塞 I/O,也叫异步 I/O,显然对应的就是阻塞式 I/O。

传统的服务器语言大多是多线程、阻塞式 I/O。这也是 Node 与众不同的地方,对于传统的服务器语言,在与用户建立连接时,每一个连接都是一个线程。 当有十万个用户连接时,服务器上就会有十万个线程。而阻塞式 I/O 是指,当一个线程在执行 I/O 操作时,这个线程会阻塞,等待 I/O 操作完成后继续执行。

举个例子可以更好理解,比如我们到一个餐馆吃饭,这个餐馆比较高级,服务员是一对一服务(每个用户都是一个线程),从我们坐下开始,服务员就把菜单给你,然后在旁边等你点菜(等待 I/O 操作),当你看完菜单,把要点的菜告诉服务员( I/O 操作结束后线程继续执行)。在你看菜单的过程中,服务员其实是被闲置的,如果你一直看,他就会一直等,直到你点完( I/O 操作结束)。这就是阻塞式 I/O。

阻塞式 I/O 必须要多线程,就这个例子来看,如果只有一个服务员(单线程),那当店里同时有十个顾客时,他需要一个一个等待顾客看菜单然后点菜,那后面的顾客要等待的时间就会很长,显然这个餐馆的服务就会很差(服务器性能差)。

所以传统的服务器都是多线程、阻塞式 I/O,也就是相当于有多个服务员(线程),每个进来的顾客都分配到一个服务员(线程),然后你看菜单时在旁边等候(阻塞式 I/O )。很明显这样的服务是让顾客最舒服的,但是对于餐馆老板来说很难受,因为要雇佣大量的服务员。因此传统的方式成本是比较大的。要有性能足够好的服务器才能支撑大量的线程。但他的优点也很明显,比如某一桌客人与服务员发生争吵(线程崩了!),不会影响到其他顾客,因为每一桌都有专门的服务员负责,因此只会对当前用户产生影响。

上面的例子应该可以很好地理解多线程、阻塞式 I/O 。而 node 的特性是单线程、非阻塞时 I/O 。node 最大的优势就是性能强,同样的服务器性能使用 node 可以比传统的服务器语言多容纳一百倍的用户(对于不同的任务有不同的差别, I/O 操作越多,node优势越明显,如果都是 CPU 计算任务,那他俩几乎没有区别(上面的例子中,忽略顾客的看菜单时间)。

还是用上面的例子再比喻一下单线程、非阻塞式 I/O 。这应该是个规模比较小的餐馆,或者说老板比较穷,雇不起大量的服务员,因此只能雇佣一个服务员。当有顾客来时,服务员把菜单送过去,顾客开始看菜单( I/O 操作),这个时候,服务员是被释放了的,他不用等待顾客看菜单,服务员说:“您先看着菜单,点好了叫我”(回调函数)。这个时候这个服务员就可以抽身去服务其他的顾客。用这种模式的话,一个服务员就可以服务多位顾客,而且不需要等待 I/O ,只需要随时监听就行了,顾客点完后会主动叫服务员(执行回调函数)。

单线程、非阻塞式 I/O 的优势就是性能强,一个人服务员就可以解决大量顾客。但是他的缺点也很明显,比如有一桌顾客和服务员又吵架了(线程崩了!),那这些顾客就都完了,因为所有人都在等这一个服务员。也就是说,如果线程崩掉了,那与这个服务器连接的所有用户都会崩溃。

通过上面的介绍,应该能深刻的理解传统方式与 node 的区别。两者之间并没有强弱,node 更不可能替代传统的服务器语言。两者所要处理的问题是不同的,node 擅长 I/O 操作多的任务,但是由于安全性差(一个崩全都崩),所以一般大公司核心功能、或者银行证券等重要部分,不会使用 node。但是对于一些小公司(用户少,资金少),node是非常合适的。另外有很多大公司的个别业务( I/O 操作很多的任务),也会使用 node,比如百度的表单收集

但是,还有一个问题就是,如果有多个顾客都在叫你,该如何响应?肯定要有一定的规则,这就涉及到了 node 的事件驱动。所以说,这三大特点,其实也是一个事,缺任何一个都不行。


代码

a.js

var fs = require('fs')

var data = fs.readFileSync('input.txt')

console.log(data.toString());
console.log("执行结束");

b.js

var fs = require('fs')

fs.readFile('input.txt',function (err, data) {
    if (err) {
        return console.error(err);
    }
    console.log(data.toString());
})

console.log("执行结束");

上面两个代码中,a 是使用的同步 I/O ,也就是阻塞式 I/O ,因此等待文件操作结束后才继续向下执行。b 中使用的就是异步 I/O,也叫非阻塞式 I/O ,当执行文件操作时,会继续向下执行,因此先打印了执行结束

  • 28
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 14
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 14
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值