(一)v8引擎?
V8引擎
首先,学习node.js一定要了解V8引擎,他是一个可以把js直接编译成(处理器可以识别的)机器码的东西。
再详细点,V8是一个
- 开源的
- 用C++写的
- 根据ECMA标准实现JavaScript
- 可以把JavaScript编译成处理器可以识别的机器码
- 可以独立运行
- 也可以嵌入其他C++应用的JavaScript引擎。
(二)Node.js与V8引擎
普通青年使用V8:运行V8, JavaScript -> V8 —compile—> Machine Code
文艺青年则有这样一个大胆的想法:
那些普通的js方法太没意思了,能力优先。如果我可以写一些C++代码,当做Add-on加到V8上,这样V8就有能力识别具有更多的Javascript命令了,就更强大了。比如说file相关的东西,本来js不能做,现在我用c++在底部实现好,然后告诉V8,当用户在js中写道file.open(xxx)的时候,就来用c++执行file open的功能,这样你的js(二b)就是有处理文件能力的js(牛b)了。
原来这个js啊还是很高层的语言啦,你想让他操纵io,他也没那能力啊,因此呀,只能通过c++来搞底层实现啦
文艺青年的想法其实就是我们的Node.js:一个把V8引擎嵌进去的C++应用,这个C++应用实现了超级多的customized新功能,这些功能使得这个应用(Node.js)非常的适合服务器开发。
所以我们讲什么是node.js,,就是一个嵌套了v8的c++应用(c++用于实现底层io功能,v8用于执行js)
服务器开发都需要什么新功能呢 === Node.js实现的新功能都包括哪些方面呢:
嗯,都是用c++写的。。。。。。。。。。。。。。。
(三)Node.js架构
- 管理可复用代码
- 处理文件
- 处理数据库
- 互联网通信
- 接受request,发送respond
- 处理需要一定时间才能完成的工作
第一层是C++ core,就是那些新加的customized功能 (其他讲解中,还会提到比如event loop,libuv等,这些之后说)。
第二层是JS core,这一层用js实现,基于/调用C++ core,让用户可以更好的使用那些C++功能,同时也实现了许多常用的功能。
在node.js源码中,C++ core是在src文件夹内。JS core是在lib文件夹内。由此可见二者的层级关系。
(四)从js到c++经历了什么?
- 你写的js代码调用了node.js的JS core (比如 fs相关功能:github.com/nodejs/node…),并且你设定了一个回调函数
- JS core部分调用了C++ core (fs.js 调用的其中一个.cc: github.com/nodejs/node…)
- C++ core调用libuv
- C++ core调用libuv的方法来封装请求对象(异步I/O过程中重要的中间产物,中间指的是从js到os之间),其中包含了异步I/O中最重要的东西之一:回调函数。
- libuv把封装好的请求对象发到OS
- 发到OS中的线程池(thread pool)等待被执行
- 线程池中某一线程将发来的请求对象中包含的I/O操作进行执行,结果存在该请求对象的req->result属性中。然后提交完成状态,也就是类似通知说"我完成了!”,之后把线程归还给线程池
- 处于完成状态的请求对象,被观察者(图中的两个小人儿,不同类型的事件有不同的观察者)在事件循环(Event Loop:大while循环,每个循环叫一个tick)中提出来(通过libuv中方法来检查是否有执行完的请求),然后放到队列(Completed Events Queue)中
- 事件循环从观察者的队列中取出处于完成状态请求对象
- 取出其中包含的I/O操作执行结果以及回调函数,发到V8中执行。到这就达到了调用#1中设定的回调函数的目的。
所谓的特点,就是Node.js是如何解决服务器高性能瓶颈问题的。
注
a. V8引擎执行的JavaScript是同步的(sync)(stackoverflow.com/questions/2…)且单线程
b. #1-#9 与 #10 是同时在工作的,I/O事件一个一个执行,然后最后发送到V8引擎中一个一个(因为JS是同步的)的执行回调函数
c. 由于b,整个Node.js有了异步I/O的能力
d. 事件驱动(event driven)、非阻塞(non-blocking)I/O的特点也就可以解释了。事件驱动就是指的#7-#10,事件被完成触发了之后各个步骤,直到最后执行回调函数。非阻塞I/O就是整个#1-#10这个过程,我们在V8中执行的JavaScript代码并不会因为事件的执行而停止,#1-#9和#10同时工作,一个执行I/O事件,一个得到通知执行回调函数。
e. 从d的解释中,可以更好的理解上一篇文章中最后那段说Node.js是单线程/多线程。
下次写事件(Event)和事件发射器(Event Emitter)相关的东西。欢迎大家交流,指正~
(五)node.js和java的多线程?并发访问量?
单线程
在Java、PHP或者.net等服务器端语言中,会为每一个客户端连接创建一个新的线程。而每个线程需要耗费大约2MB内存。也就是说,理论上,一个8GB内存的服务器可以同时连接的最大用户数为4000个左右。要让Web应用程序支持更多的用户,就需要增加服务器的数量,而Web应用程序的硬件成本当然就上升了。
Node.js不为每个客户连接创建一个新的线程,而仅仅使用一个线程。当有用户连接了,就触发一个内部事件,通过非阻塞I/O、事件驱动机制,让Node.js程序宏观上也是并行的。使用Node.js,一个8GB内存的服务器,可以同时处理超过4万用户的连接。
另外,带线程的带来的好处,还有操作系统完全不再有线程创建、销毁的时间开销。
坏处,就是一个用户造成了线程的崩溃,整个服务都崩溃了,其他人也崩溃了。
多线程、单线程的一个对比。
也就是说,单线程也能造成宏观上的“并发”。
非阻塞I/O non-blocking I/O-------线程执行线程的,io执行io的,两者并行
例如,当在访问数据库取得数据的时候,需要一段时间。在传统的单线程处理机制中,在执行了访问数据库代码之后,整个线程都将暂停下来,等待数据库返回结果,才能执行后面的代码。也就是说,I/O阻塞了代码的执行,极大地降低了程序的执行效率。
由于Node.js中采用了非阻塞型I/O机制,因此在执行了访问数据库的代码之后,将立即转而执行其后面的代码,把数据库返回结果的处理代码放在回调函数中,从而提高了程序的执行效率。
当某个I/O执行完毕时,将以事件的形式通知执行I/O操作的线程,线程执行这个事件的回调函数。为了处理异步I/O,线程必须有事件循环,不断的检查有没有未处理的事件,依次予以处理。
阻塞模式下,一个线程只能处理一项任务,要想提高吞吐量必须通过多线程。而非阻塞模式下,一个线程永远在执行计算操作,这个线程的CPU核心利用率永远是100%。所以,这是一种特别有哲理的解决方案:与其人多,但是好多人闲着;还不如一个人玩命,往死里干活儿。
(六)node.js适合做?
少用cpu,多用io的应用
当应用程序需要处理大量并发的I/O,而在向客户端发出响应之前,应用程序内部并不需要进行非常复杂的处理的时候,Node.js非常适合。Node.js也非常适合与web socket配合,开发长连接的实时交互应用程序。
(七)对node.js单线程的误解?node.js工作时真的只有一个线程吗?
为什么单线程却能够支持高并发?
(1)前提:I/O密集型任务
(2)单线程的解释:主线程一个,底层工作线程多个。
(3)事件机制的底层依赖库:libuv、libeio、libev
libuv:开发node过程中需要跨平台,首选为Linux使用libev(底层 为epoll),备选Windows使用IOCP实现,用于抽象libev和IOCP的高性能网络库。
非阻塞TCP、非阻塞命名管道、UDP、异步DNS
异步文件系统、路径查找、ANSI转义、文件系统事件
子进程生成、线程池调度、进程间IPC与套接字共享
高分辨率时间、定时器
epoll和IOCP的区别:
epoll用于Linux系统,IOCP用于Windows系统;
epoll是同步非阻塞模型:当事件资源满足时发出可处理通知消息(主线程需要自己去处理);IOCP是异步非阻塞模型,当事件完成时发出通知消息(工作线程帮主线程处理完了)
(4)nodejs运行机制:
a、V8引擎解析JavaScript脚本
b、解析后的代码,调用Node API
c、libuv库负责Node API的执行。它将不同的任务分配给不同的线程,形成一个Event Loop(事件循环),以异步的方式将任务的执行结果返回给V8引擎
d、V8引擎再将结果返回给用户、
(八)单线程的利弊
-
单线程的好处:
(1)多线程占用内存高
(2)多线程间切换使得CPU开销大
(3)多线程由内存同步开销
(4)编写单线程程序简单
(5)线程安全 -
单线程的劣势:
(1)CPU密集型任务占用CPU时间长
(2)无法利用CPU的多核
(3)单线程抛出异常使得程序停止
(九)node.js真的是单线程吗?
-
Node.js的单线程并不是真正的单线程,只是开启了单个线程进行业务处理(cpu的运算),同时开启了其他线程(io线程)专门处理I/O。当一个指令到达主线程,主线程发现有I/O之后,直接把这个事件传给I/O线程,不会等待I/O结束后,再去处理下面的业务,而是拿到一个状态后立即往下走,这就是“单线程”、“异步I/O”。
(十)node.js如何解决的高并发?
如何解决高并发?
node使用异步IO和事件驱动(回调函数)来解决这个问题。
一般来说,高并发解决方案会提供多线程模型,为每个业务逻辑提供一个线程,通过系统线程切换来来弥补同步I/O调用的时间开销。像apache,是一个请求一个线程。