Node.js 是什么
简单的说 Node.js 就是运行于服务端的JavaScript解释器,既然是解释器,有很多地方也把Node.js 叫成运行环境。为什么强调服务端呢?其实是相对于浏览器客户端来说的,之前的javaScript就是负责浏览器客户端程序的开发,而Node.js打破了这个禁制,使用javaScript可以快速构建网络服务及应用,如向客户端提供接口。这对于熟悉和重点使用javaScript的程序员和公司来说是意义重大的,搭建一些服务的成本是能够降低的。比如因此出现的成为“全栈工程师”的一条新路,就不在这里讨论了。
解释器的一些知识可以参考:java的编译器和解释器浅析
Chrome V8 引擎
如果大家去查Node.js的资料,经常会出现这个词:V8引擎。原话基本上都是这样的 —— “Node.js 是一个基于 Chrome V8引擎的JavaScript运行环境。” 那什么是V8引擎呢?
Chrome V8引擎是一个JavaScript VM,在运行JavaScript之前,相比其它的JavaScript的引擎转换成字节码或解释执行,V8采用即时编译技术(JIT),直接将JavaScript代码编译成本地平台的机器码,然后直接交由硬件执行,并且使用了如内联缓存(inline caching)等方法来提高性能。
有了这些功能,JavaScript程序在V8引擎下的运行速度媲美二进制程序。
Node.js 和Chrome V8 引擎的关系
从解释器这个角度讲,我认为Node.js == V8引擎;但整体的看,实际上它是对V8进行了封装及优化。Node对一些特殊用例进行了优化,提供了替代的API,使得V8在非浏览器环境下运行得更好。例如,在服务器环境中,处理二进制数据通常是必不可少的,但Javascript对此支持不足,因此,Node增加了Buffer类,方便并且高效地 处理二进制数据。因此,Node不仅仅简单的使用了V8,还对其进行了优化,使其在各环境下更加给力。
Node.js架构
Node.js 使用了一个事件驱动、非阻塞式I/O的模型。在这里我要强调,语言和框架固然重要,然是其中的设计思想才是我们要重点记住的,因为互通的就是这些思想,并不是代码本身。
Node.js 标准库,这部分是由 Javascript编写的,即我们使用过程中直接能调用的 API。
Node bindings,这一层是 Javascript与底层 C/C++ 能够沟通的关键,前者通过 bindings 调用后者,相互交换数据。
最下面这一层是支撑 Node.js 运行的关键,由 C/C++ 实现。
- V8: Google 推出的 Javascript VM,也是 Node.js 为什么使用的是 Javascript的关键,它为 Javascript提供了在非浏览器端运行的环境,它的高效是 Node.js 之所以高效的原因之一。
- Libuv: 它为 Node.js 提供了跨平台,线程池,事件池,异步 I/O 等能力,是 Node.js 如此强大的关键。
- C-ares: 提供了异步处理 DNS 相关的能力。
- http_parser、OpenSSL、zlib 等: 提供包括 http 解析、SSL、数据压缩等其他的能力。
想要深入了解Node.js,几个词要记住并了解
- RESTful API
- 单线程
Node.js 并没有给 Javascript 执行时创建新线程的能力,它的执行线程是单线程的。但这并不会影响它对任务进行并发处理,通过非阻塞操作和事件驱动就可以实现并发。 -
非阻塞IO & 事件驱动
事件驱动
- 每个Node.js进程只有一个主线程在执行程序代码,形成一个执行栈(execution context stack)。
- 主线程之外,还维护了一个"事件队列"(Event queue)。当用户的网络请求或者其它的异步操作到来时,node都会把它放到Event Queue之中,此时并不会立即执行它,代码也不会被阻塞,继续往下走,直到主线程代码执行完毕。
- 主线程代码执行完毕完成后,然后通过Event Loop,也就是事件循环机制,开始到Event Queue的开头取出第一个事件,从线程池中分配一个线程去执行这个事件,接下来继续取出第二个事件,再从线程池中分配一个线程去执行,然后第三个,第四个。主线程不断的检查事件队列中是否有未执行的事件,直到事件队列中所有事件都执行完了,此后每当有新的事件加入到事件队列中,都会通知主线程按顺序取出交EventLoop处理。当有事件执行完毕后,会通知主线程,主线程执行回调,线程归还给线程池。
- 主线程不断重复上面的第三步。
总结:
我们所看到的node.js单线程只是一个js主线程,本质上的异步操作还是由线程池完成的,node将所有的阻塞操作都交给了内部的线程池去实现,本身只负责不断的往返调度,并没有进行真正的I/O操作,从而实现异步非阻塞I/O,这便是node单线程和事件驱动的精髓之处了。
一个服务的搭建实例
将以下代码存储为js文件并运行,在浏览器中打开 http://127.0.0.1:8888/ ,你的第一个node应用就能使用了。
var http = require('http');
http.createServer(function (request, response) {
// 发送 HTTP 头部
// HTTP 状态值: 200 : OK
// 内容类型: text/plain
response.writeHead(200, {'Content-Type': 'text/plain'});
// 发送响应数据 "Hello World"
response.end('Hello World\n');
}).listen(8888);
// 终端打印如下信息
console.log('Server running at http://127.0.0.1:8888/');
最后我们看2个有意思的程序,大家结合学到的知识,猜猜打印的结果可能是什么样子的
第一个:
for (var i = 0; i <= 5; i++) {
setTimeout(function() {
console.log( i );
}, i*1000);
console.log( ' i : ' , i );
}
console.log( ' i i: ' , i );
第二个:
// 获取当前时间戳
var start = Date.now();
// 2秒后获取时间间隔
setTimeout(function () {
console.log(Date.now() - start);
}, 2000);
// 1秒后获取时间间隔并开始运行长循环
setTimeout(function () {
console.log(Date.now() - start);
for (var i = 0; i < 1000000000; i++) {
// 执行长循环
}
}, 1000);
//
var fs = require("fs");
fs.readFile('input.txt', function (err, data) {
if (err) return console.error(err);
console.log(data.toString());
});
console.log("程序执行结束!");
感谢以下借鉴的大牛文章
https://www.runoob.com/nodejs/nodejs-http-server.html
https://blog.csdn.net/xiahuale/article/details/87005901
https://baike.baidu.com/item/node.js/7567977?fromtitle=nodejs&fromid=11244313&fr=aladdin
网友评论