node是什么
- Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行环境。
- Node.js 使用了一个事件驱动、非阻塞式 I/O 的模型。
- Node使用包管理器NPM。
第一句话
Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行环境。
运行环境, 不是一门语言,不是一个框架。只是能够作为JavaScript代码运行的一个环境。
而这个运行环境主要是由V8提供的。
V8做了什么?
创建了一个callstack。
function main(){
func1();
}
function func1(){
func2();
}
function func2(){
console.log(1);
}
main();
除去V8,Node中还有哪些东西?
除去V8,Node中另外一个比较重要的组成就是 libuv。
What? libuv
是什么鬼?
先说说,关于Node的另外一句话:
Node is designed to build scalable network applications.
这句话的底气在哪儿,就是Node本身采用的 事件驱动,非阻塞I/O模型。
在 并发模型构建网络的应用中,每个连接都会生成一个新线程,每个新线程可能需要 2MB 的配套内存。在一个拥有 8 GB RAM 的系统上,理论上最大的并发连接数量是 4,000 个用户。随着您的客户群的增长,如果希望您的 Web 应用程序支持更多用户,那么,您必须添加更多服务器。所以在传统的后台开发中,整个 Web 应用程序架构(包括流量、处理器速度和内存速度)中的瓶颈是:服务器能够处理的并发连接的最大数量。这个不同的架构承载的并发数量是不一致的。而且,多个线程存在的话,也会衍生出线程切换的问题,这也会占用一定资源。
并发存在的两个问题:
- Execution stacks take up memory: 资源有限
- Context switching is not free:占用额外资源
Node的解决这个问题思路:
1. 在网络应用中,比较慢的环节主要在 磁盘读取 或 网络请求阶段,这时候CPU处于闲置状态。
2. 如果在等待I/O操作时,能够释放处于闲置状态的CPU,则可以很大程度上利用资源;
3. 那么接下来的问题就在于,如何在I/O 操作完成后,继续执行后续的操作;
4. 解决方案:事件驱动,采用回调。这和浏览器中的Event Loop是一样的道理。
市场上已经有一些使用同样处理思路的框架。
- EventMachine:处理网络请求的框架,主要是面向ruby;
- Twisted:用Python实现的基于事件驱动的网络引擎框架;
具体是怎么做的?
在程序运行时,V8 会把I/O操作等耗时较多操作及相关回调一并交给libuv去处理。而V8继续执行后面的代码。等到I/O操作完成后,libuv会将回调方法放到事件队列中。
const fs = require('fs');
const readFile = (file) => {
fs.readFile(file, (err, data) => {
if(!err) console.log(data);
});
}
console.log('program start ......');
readFile('file.json');
console.log('readFile has put the I/O ');
console.log('program end!!!!!');
负责异步程序调度的工作就是libuv
做的事情。
Libuv is a multi-platform support library with a focus on asynchronous I/O.
在上述程序中,当遇到文件读取操作时,V8会把js接口转成C++接口 同时把回调方法一并交给libvu。此时libuv接管这个文件读取任务。当文件读取完成后,libuv就会把这个事件的回调函数扔到事件队列里。当下一次检查事件队列时,就会执行该回调函数。
question: 既然这样,怎么理解node中的单线程?
再捋一捋Node, V8 和libuv的关系。
1) Node主要由V8 javascript引擎和libuv组成;
2) v8引擎主要负责解释执行js代码,碰到需要异步的操作会交给libuv处理;
3) libuv本身是独立的c语言库,可以直接使用c/c++来调用;
在浏览器端,JS是没有能力进行文件读取操作的。js最初的能力也就限制在表单校验上。而在Node中,