书接上次:《我与Node.js重新认识的第一周 - Node.js 风格特点》。这次读了一些关于底层实现的东西:
  1. 《深浅》第3章 异步I/O - node.js是如何实现异步i/o的
  2. Udemy 《Learn and Understand NodeJS Learn and Understand NodeJS》 Section 2&3

 

V8引擎

首先,学习node.js一定要了解V8引擎,他是一个可以把js直接编译成(处理器可以识别的)机器码的东西。

再详细点,V8是一个
  1. 开源的
  2. 用C++写的
  3. 根据ECMA标准实现JavaScript
  4. 可以把JavaScript编译成处理器可以识别的机器码
  5. 可以独立运行
  6. 也可以嵌入其他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)了。
 
文艺青年的想法其实就是我们的Node.js:一个把V8引擎嵌进去的C++应用,这个C++应用实现了超级多的customized新功能,这些功能使得这个应用(Node.js)非常的适合服务器开发。
 
服务器开发都需要什么新功能呢 === Node.js实现的新功能都包括哪些方面呢:
  1. 管理可复用代码
  2. 处理文件
  3. 处理数据库
  4. 互联网通信
  5. 接受request,发送respond
  6. 处理需要一定时间才能完成的工作

 

Node.js架构

  1. 第一层是C++ core,就是那些新加的customized功能 (其他讲解中,还会提到比如event loop,libuv等,这些之后说)。
  2. 第二层是JS core,这一层用js实现,基于/调用C++ core,让用户可以更好的使用那些C++功能,同时也实现了许多常用的功能。
在node.js源码中,C++ core是在src文件夹内。JS core是在lib文件夹内。由此可见二者的层级关系。
 

Node.js 异步I/O

Node.js如何实现异步 I/O, 从JS到OS到底发生了哪些步骤(这个是udemy+《深浅》的合体版总结)

  1. 你写的js代码调用了node.js的JS core (比如 fs相关功能:github.com/nodejs/node…),并且你设定了一个回调函数
  2. JS core部分调用了C++ core (fs.js 调用的其中一个.cc: github.com/nodejs/node…)
  3. C++ core调用libuv
  4. C++ core调用libuv的方法来封装请求对象(异步I/O过程中重要的中间产物,中间指的是从js到os之间),其中包含了异步I/O中最重要的东西之一:回调函数。
  5. libuv把封装好的请求对象发到OS
  6. 发到OS中的线程池(thread pool)等待被执行
  7. 线程池中某一线程将发来的请求对象中包含的I/O操作进行执行,结果存在该请求对象的req->result属性中。然后提交完成状态,也就是类似通知说"我完成了!”,之后把线程归还给线程池
  8. 处于完成状态的请求对象,被观察者(图中的两个小人儿,不同类型的事件有不同的观察者)在事件循环(Event Loop:大while循环,每个循环叫一个tick)中提出来(通过libuv中方法来检查是否有执行完的请求),然后放到队列(Completed Events Queue)中
  9. 事件循环从观察者的队列中取出处于完成状态请求对象
  10. 取出其中包含的I/O操作执行结果以及回调函数,发到V8中执行。到这就达到了调用#1中设定的回调函数的目的。
注:
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)相关的东西。欢迎大家交流,指正~