昨天写了篇博客,介绍了一下我对node.js的第一次亲密接触后的感觉,觉得node.js很小众,出乎我意料不少人感兴趣,而且对博客中的细节问题作了评论,最多的是围绕node.js的异步与单线程展开的,固然还有不少关于node.js到底是不是语言?不是的话又是什么。。。之类的问题,其实刚接触node.js,了解的并非很深刻,越是回复你们问题,内心越是没底,决定认真研究一下,经人指点看了一下《Node.js开发指南》发现大部分问题都有了答案,权当一个读书笔记把问题答案分享出来,但愿能够帮到一些和我同样才接触node.js的小菜html
在上篇博客中提到咱们使用node.js写的JavaScript代码是单线程运行的,让不少同窗很疑惑,单线程怎么实现异步操做,单线程谁去响应事件。。。在html5 Web Workers中我也有提到过客户端的JavaScript也是单线程运行的,你们明显没有这么大反应,仍是广泛能接受的。可单线程的客户端JavaScript也能响应DOM事件,还有你们都很熟悉的ajax操做,回调函数也是异步的,既然客户端JavaScript是单线程执行的,回调函数是谁调用的呢?答案很简单,JavaScript的宿主环境——浏览器,也就是说虽然JavaScript是单线程执行的,但浏览器是多线程的,负责调度管理JavaScript代码,让它们在恰当的时机执行。html5
因此咱们所说的node.js单线程,是指node.js并无给咱们建立一个线程的能力,全部咱们本身写的代码都是单线程执行的,在同一时间内,只能执行咱们写的一句代码。但宿主环境node.js并非单线程的,它会维护一个执行队列,循环检测,调度JavaScript线程来执行。所以单线程执行和并发操做并不冲突。node
什么叫阻塞(block)?线程在执行中若是遇到I/O操做(磁盘读写、网络通讯等)一般须要耗费较长的时间,这时候操做系统会剥夺线程对CPU的控制权,使其暂停,并把资源让给其它的工做线程,这种线程调度方式成为阻塞。当I/O操做完毕的时候操做系统将这个线程的阻塞状态解除,恢复其对CPU的控制权,令其继续执行,这种I/O模式就是同步I/O或成为阻塞I/O。git
响应的异步I/O或非阻塞I/O则针对全部的I/O操做采起不阻塞的策略,当线程遇到I/O操做时不会以阻塞的方式等待I/O操做结束,而只是将I/O请求发送给操做系统,继续执行后续语句。当操做系统完成I/O操做时以事件的形式通知执行I/O操做的线程,线程会在特定时间处理这个事件。为了处理异步I/O必须有事件循环,不断检查有没有未处理的事件,依次予以处理。github
在阻塞模式下,一个线程只能处理一个任务,要想提升吞吐量必须经过多线程。而在非阻塞模式下一个线程永远在执行计算操做,这个线程所使用的CPU核心利用率永远是100%,I/O以事件的方式通知。在阻塞模式下多线程每每可以提升系统吞吐量,由于一个线程阻塞时还有其余线程在工做,多线程何以让CPU资源不被阻塞的线程浪费。而在非阻塞模式下,线程不会被I/O阻塞,永远在利用CPU。异步I/O减小了多线程中建立线程、分配内存、列入调度、切换线程、内存换页、CPU缓存等方面的开销。web
上面提到了几回事件循环机制,那么这个听起来貌似很高端的东东到底是什么呢?所谓事件循环是指node.js会把全部的异步操做使用事件机制解决,有个线程在不断地循环检测事件队列。node.js中全部的逻辑都是事件的回调函数,因此node.js始终在事件循环中,程序入口就是事件循环第一个事件的回调函数。事件的回调函数中可能会发出I/O请求或直接发射( emit)事件,执行完毕后返回事件循环。事件循环会检查事件队列中有没有未处理的事件,直到程序结束。node.js的事件循环对开发者不可见,由libev库实现,libev不断检查是否有活动的、可供检测的事件监听器,直到检查不到时才退出事件循环,程序结束。ajax
关于node.js到底是什么,你们的问题在于数据库
很遗憾,这三个问题的答案都是NO,看看官方对本身的描述浏览器
Node.js is a platform built on Chrome's JavaScript runtime for easily building fast, scalable network applications. Node.js uses an event-driven, non-blocking I/O model that makes it lightweight and efficient, perfect for data-intensive real-time applications that run across distributed devices.缓存
官方很明确地说node.js是一个platform,也就是一个作xxx的平台。node.js是一个能够在服务器端运行JavaScript的平台,其实这句话华也不许确,按照《JavaScript权威指南》《JavaScript高级程序设计》等书中的定义,JavaScript是由两部分组成
而只有core JavaScript能够在node.js上运行,因此node.js借用了JavaScript的语法,但并不能用来处理浏览器对象(BOM)及文档对象(DOM),因此node.js并非设计为在服务器端运行解析html文档的(固然有module能够作此事),因此“在服务器端运行的JavaScript”在必定程度上误导了初学者。
同时node.js并不只仅运行core JavaScript,node.js中还有文件系统、模块包、操做系统API、网络通讯、二进制类型处理等core JavaScript不具有的功能。
从字面意思上是的,由于咱们在使用node.js开发的时候写的确实是JavaScript语句,并且node.js利用Google的V8 Javascript 引擎来解析JavaScript语句,但系统真正调用执行的代码是用C++写的,咱们作的只是用JavaScript语句来调用这些底层API,因此不用担忧其执行效率太低问题
绝不夸张的说node.js最大的特定就是采用异步I/O和事件驱动架构,对于高并发解决方案传统架构师多线程模型,为每一个业务逻辑童工一个线程,经过系统线程切换来来弥补同步I/O调用的时间开销。node.js使用的是单线程模型,对全部I/O都采用异步式的请求方式,避免频繁的上下文切换,在node.js执行的时候维护着一个事件队列,程序在执行时进入事件循环等待下一个事件到来,每一个异步I/O请求完成后都会被推送到事件队列中的等待执行。
对于一个简单的数据库访问操做,传统方式是这样实现的
res = db.query('SELECT * from some_table'); res.output();
代码执行到第一行的时候线程会阻塞,等待query返回结果,而后继续处理。因为数据库查询、磁盘读写、网络通讯等缘由阻塞时间会很是大(相对于CPU始终频率)。对于高并发的访问,一方面线程长期阻塞等待,另外一方面为了应付新情求而不断添加新线程,会浪费大量系统资源,同时线程的增长也会也会占用大量的CPU时间来处理内存上下文切换。看看node.js怎么处理
db.query('SELECT * from some_table', function(res) {
res.output(); });
在代码中熟悉Javascript的同窗一眼就能够看明白query的第二个参数是一个回调函数,进程执行到db.query的时候不会等待结果返回,而是直接继续执行下面的语句,直到进入事件循环。当数据库执行结果返回的时候会将事件发送到事件队列,等到线程进入事件循环后才会调用以前的回调函数。
node.js的异步机制是基于事件的,全部的I/O、网络通讯、数据库查询都以非阻塞的方式执行,返回结果由事件循环来处理。node.js在同一时刻只会处理一个事件,完成后当即进入事件循环检查后面事件。这样CPU和内存在同一时间集中处理一件事,同时尽可能让耗时的I/O等操做并行执行。
node.js用异步式I/O和事件驱动代替多线程提高性能,除了使用高效的V8做为JavaScript引擎外还使用了高效的libev和libeio库支持事件驱动和异步I/O。
node.js做者在libeio和libev的基础上抽象出了libuv层,对于POSIX(Portable Operating System Interface 是一套操做系统API规范,遵循的有Unix、Linux、Mac OS X等)操做系统libuv经过封装libev和libio来利用epoll或kqueue。而在Windows下libuv使用了IOCP(Input/Output Completion Port,输入输出完端)机制 在不一样平台下实现高性能。
了解了这个就不要再觉得node.js是利用JavaScript来操做系统了
其实这个问题能够归结为node.js有什么特长,除了语法上让熟悉JavaScript的人很舒服。相对于Javascript为客户端而生,node.js为网络而生,一切都以http为主,其内建HTTP服务器支持,使用node.js能够轻易地搭建一个网站和服务器组合,而不用想使用PHP还须要额外的Apache服务器,经过特有模块或CGI调用才能将PHP脚本结果返回给用户。
node.js还能够部署到非网络应用环境下,由于其能够调用C/C++代码,充分利用现有函数库,在性能上有很大优越性。
在这些场景下使用node.js是很是合适的
博客中基本理论知识都来源于《Node.js开发指南》,甚至不少篇幅都是直接使用原话,没有抄袭据为己有的意思,只是做者说的太明白了,但愿对初入node.js的朋友有帮助,固然若是感兴趣能够直接购买原书。
但愿唠唠叨叨这么多,可以对对以前博客持有疑问的博友有所帮助,最后在唠叨一句,单线程执行是指咱们写的JavaScript语句在同一时刻只能执行一句,而不是node.js是单线程,其实咱们的异步I/O及事件循环都是另外线程在作。
固然对于一些CPU密集的操做在node.js里面也有process.nextTick()这样的解决方案或者直接使用C++处理,研究明白了和你们分享,也有人不满意node.js的单线程写了本身的module来让node.js多线程,感兴趣的同窗能够看看node-threads-a-gogo
因为刚刚接触node.js,文中谬误颇多,但愿多家多多批评指教