node.js极速入门课程:进入学习
我们日常工作中或多或少听说过以下的话:
Node是一个
非阻塞I/O
(non-blocking I/O)和事件驱动
(event-driven)的JavaScript运行环境
(runtime),所以它非常适合用来构建I/O密集型应用,例如Web服务等。
不知道当你听到类似的话时会不会有和我一样的疑惑:单线程的Node为什么适合用来开发I/O密集型应用?
按道理来说不是那些支持多线程的语言(例如Java和Golang)做这些工作更加有优势吗?
要搞明白上面的问题,我们需要知道Node的单线程指的是什么。【相关教程推荐:nodejs视频教程】
Node不是单线程的
其实我们说Node是单线程的,说的只是我们的JavaScript代码
是在同一个线程(我们可以叫它主线程
)里面运行的,而不是说Node只有一个线程在工作
。实际上Node底层会使用libuv的多线程能力
将一部分工作(基本都是I/O相关操作)放在一些主线程之外
的线程里面执行,当这些任务完成后再以回调函数
的方式将结果返回到主线程的JavaScript执行环境。可以看看示意图:
注: 上图是Node事件循环
(Event Loop)的简化版,实际上完整的事件循环会有更多的阶段例如timers等。
Node适合做I/O密集型应用
从上面的分析中我们知道Node会将所有的I/O操作通过libuv的多线程能力分散到不同的线程里面执行,其余的操作都放在主线程里面执行。那么为什么这种做法就比Java或者Golang等其它语言更适合做I/O密集型应用呢?我们以开发Web服务为例,Java和Golang等主流后端编程语言的并发模型是基于线程
(Thread-Based)的,这也就意味他们对于每一个网络请求都会创建一个单独的线程
来处理。可是对于Web应用来说,主要还是对数据库的增删改查,或者请求其它外部服务等网络I/O操作
,而这些操作最后都是交给操作系统的系统调用来处理的(无需应用线程参与),并且十分缓慢(相对于CPU时钟周期来说)
,因此被创建出来的线程大多数时间是无事可做
的而且我们的服务还要承担额外的线程切换
开销。和这些语言不一样的是Node没有为每个请求都创建一个线程,所有请求的处理
都发生在主线程中,因此没有了线程切换
的开销,并且它还会通过线程池
的形式异步处理这些I/O
操作,然后通过事件的形式告诉主线程结果从而避免阻塞主线程的执行,因此它理论上
是更高效的。这里值得注意的是我只是说Node理论上
是更快的,实际上真不一定。这是因为现实中一个服务的性能会受到很多方面的影响,我们这里只是考虑了并发模型
这一个因素,而其它因素例如运行时消耗也会影响到服务的性能,举个例子,JavaScript
是动态语言,数据的类型需要在运行时进行推断,而Golang
和Java
都是静态语言它们的数据类型在编译时就可以确定,所以它们实际执行起来可能会更快,占用内存也会更少。