一、Node.js的整体感知
Node.js是JavaScript的一个服务端运行环境。使得JS可以像PHP、Python等语言一样可以进行服务端程序开发。
传统JS中的DOM和BOM被剔除,首先它遵循EMCAScript标准实现核心JavaScript,在此基础上,又实现了诸如模块、包、文件系统、网络通信和操作系统API等新的功能。
Node内部采用Google Chrome的V8引擎,作为JavaScript语言的解释器,在该引擎当中执行ES代码,另外Node平台还提供了一些组件,通过自行开发的Libuv库,用于调度操作系统资源。
二、Node.js在Web当中的作用
1、 做动态网站
2、分发数据请求,渲染HTML页面
三、Node.js的核心特性(事件驱动和非阻塞)
首先先了解一下进程和线程的概念。
进程:操作系统为应用程序分配资源的一个单位。用来给应用程序提供一个运行环境。
线程:用来执行应用程序中的代码,一个进程内部,可以有很多线程。但在一个线程内部,同时只能做一件事。
某个应用程序启动之后会默认创建一个主线程,用于执行我们的代码。但用户的代码当中通常会有一下耗费时间的阻塞代码,对于Java、.Net、PHP这种一个进程当中可以有多个线程的应用程序,在执行代码的过程当中,会开辟多个线程去分别执行那些主线程当中会发生阻塞的代码。
多线程存在的问题:
- 创建线程耗费资源
- 线程数量有限
- 线程之间共享某些数据,同步某个状态都很麻烦
- CPU在不同线程之间转换非常耗时
但对于Node来说,由于Node采用Chrome V8引擎处理JavaScript脚本,而V8最大的特点是单线程运行。即Node内部只允许有一个线程,故为了提高代码的执行效率,避免代码阻塞的情况的出现。Node当中采用大量的异步操作。即Node当中所有会发生代码阻塞的操作都是异步的。
3.1、Node当中的事件驱动模型
在Node的内部有一个事件队列,事件队列由一对一对的键值对构成,即事件:对应的回调函数的形式。Node的主线程在执行用户的代码的过程中,先执行那些非阻塞的代码,当执行到类似于文件操作或网络操作之类的阻塞代码,则会根据其任务代码出现的顺序,将其放入事件队列当中,与该任务相关的代码放在该任务的回调函数当中。
即Node主线程在执行程序的过程,会直接执行那些非阻塞的代码,但如果在执行的过程中,遇到阻塞型的代码,主线程的处理仅仅为将该事件函数及其对应的回调函数放进事件队列当中,暂不执行。等主线程把程序当中那些非阻塞的代码全部执行完了之后,再按照从上到下的顺序,从事件队列当中来取事件任务来执行。
当Node执行事件队列当中的任务时,先执行阻塞事件,然后再开始执行该事件对应的回调函数,在执行其回调的函数的过程当中,同样也是先执行那些非阻塞的代码,对于遇到回调函数当中的阻塞事件时,同样暂不执行,将该事件及其对应的回调函数,插入事件队列的尾部。
3.2、Node内部的线程池模型
Node内部有一个事件循环(Event Loop)来依次从事件队列(Event Queue)当中取事件来执行,对于事件队列的一个键值对来说,从开始执行该阻塞事件,到开始执行该事件对应的回调函数的过程当中,可能会设计文件或网络操作,可能会阻塞很长时间,但实际上代码不会卡死在这里。因为这里的阻塞操作不是由node主线程来做的,而是交给另一个线程来完成的。
在node底层维护了一个线程池,该线程池内部又维护了很多的线程,当Event Loop在依次执行事件队列当中的事件时,若遇到阻塞操作,就交给线程池当中的线程来做,这对于主线程没有任何影响,主线程仍然继续沿着Event Loop往下走,接着去处理事件队列当中的下一个事件。
不一定是等事件队列当中的上一个事件的回调函数执行完成之后,才开始执行下一个事件。
当线程池当中的线程执行完了交给它的任务之后,它会通知正处于Event Loop当中的主线程,让其来执行对应的回调函数。然后该线程得以释放,返回到线程池当中。
在Node内部实际上是多线程的,对于类似于文件操作之类的阻塞操作,让另一个线程去耗费这个时间,而主线程在一直不停的工作。一般我们说node是单线程的,是因为对于外界变成的API来说,所有的任务都是由一个线程来完成,只有内部的一些阻塞操作才交给内部的线程池来完成。
Node将所有的阻塞操作交给内部实现的线程池,而Node主线程本身则负责不停的往返调度。非阻塞最大的性能优势就是,能充分利用单核CPU的优势,如果让单核CPU不断地在多个线程之间切换,会造成很大的性能损耗。Node采用基于事件驱动的异步I/O模型,极大地提高了HTTP服务器的并发性能。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。