Node技术架构
Contents
1 Node.js概述
1.1 Node.js不是什么
- 不是web框架
(Flask,Spring)
- 不是编程语言
(Python,PHP)
1.2 Node.js是什么
- 是一个平台
- 将多种技术组合起来
- 让JavaScript也能调用系统接口,开发后端应用
- Node.js搭建技术
V8
引擎libuv
C/C++
实现的c-ares
(解析dns域名)、http-parser
(解析http)、OpenSSL
(加密解密)、zlib
(数据压缩)等库
2 Node.js技术架构
2.1 Node.js bindings
-
背景
- Node.js用C++对http_parser进行封装,使它符合某些要求(统一数据类型等),封装的文件叫做
http_parser_bindings.cpp
- 用Node.js提供的编译工具将其编译为.node文件
- JS代码可以直接require这个.node文件
- 这样JS就能调用C++库,中间的桥梁就是binding
- 由于Node.js提供了很多binding,所以加个s
- 这就是bindings
- Node.js用C++对http_parser进行封装,使它符合某些要求(统一数据类型等),封装的文件叫做
-
概念
- Bindings层主要是一些胶水代码
- 在Node.js中,Bindings层所做的就是把底层的C/C++接口暴露给JavaScript环境,从而打通JavaScript与C/C++之间的相互调用
-
JS与C++交互
-
C++调用JS回调
2.2 libuv
-
背景
- 每个系统上的异步I/O(系统与外界(硬盘文件、打印机、网络请求)交互)都不一样
- 比如FreeBSD系统上有kqueue、Linux上有epoll、Windows上有IOCP
- Ryan为了一个跨平台的异步I/O库,开始写libuv
- libuv会根据系统自动选择合适的异步方案
-
功能
- 可以用于TCP(建立HTTP服务器)/UDP(qq聊天)/DNS(网址对应的ip)/文件(读写)等的异步操作
- 在运行时负责一个事件循环(Event Loop)、一个线程池、文件系统I/O、DNS相关的I/Oy以及网络I/O等。
2.3 V8
- 功能
- 将JS源代码变成**本地代码(机器代码)**并执行
- 维护调用栈,确保JS函数的执行顺序
- 内存管理,为所有对象分配内存
- 垃圾回收,重复利用无用的内存
- 实现JS的标准库(数组的方法等)
- 注意
- V8不提供DOM API(浏览器提供)
- V8执行JS是单线程的
- 可以开启两个线程分别执行JS
- V8本身是包含多个线程的,如垃圾回收为单独线程
- 自带event loop,但Node.js基于libuv自己做了一个
2.4 Event Loop
-
什么是Event
- 事件分为内部与外部
- 计时器到期了(内部事件)
- 文件可以读取了、读取出错了(外部事件)
- socket(HTTP消息)有内容了,关闭了(外部事件)
-
什么是Loop
- loop就是循环,由于事件是分优先级的,所以处理起来也是分先后的
- 所以Node.js需要按顺序轮询每种事件
- 这种轮询往往都是循环的,1–>2–>3–>1–>2–>3
-
Event Loop
- 操作系统可以触发事件,JS可以处理事件
- Event Loop就是对事件处理顺序的管理
-
顺序示意图
- 重点阶段
- **
timers
**检查计时器 poll
轮询,检查系统事件- **
check
**检查setImmediate
回调 - 其他阶段用的较少
- **
- 注意
- 大部分时间,Node.js都停在
poll
轮询阶段 - 大部分事件都在poll阶段被处理,如文件、网络请求
- poll阶段是有停留时间限制的,不同系统停留时间不同
- 大部分时间,Node.js都停在
- setTimeout(f1, 0)和setImmediate(f2)哪个先执行?
- 由于大部分时间js都停留在poll阶段,之后进入check阶段,从而执行setImmediate;之后进入timers阶段,才会执行setTimeout
- 只有当Node.js最开始运行时,会首先进入timers阶段执行setTimeout
- 小结
- Node.js的js线程是单线程运行的,通过一个事件循环来循环取出消息队列中的消息进行处理,处理过程基本上就是去调用该消息的回调函数。
- 事件驱动程序设计(Event-Driven Programming)模型是Node.js的重要特性之一。这种模型的程序运行流程是由用户的操作(如鼠标的按键、键盘的按键操作)或者是由其他程序的消息来驱动的。
2.5 总结
- 用
libuv
进行异步I/O操作 - 基于libuv,Node.js用
Event Loop
管理事件处理顺序 - 用
C/C++库
高效处理DNS/HTTP - 用
bindings
让JS能和C/C++沟通(require) - 用
V8
运行JS - 用
Node.js标准库
简化JS代码
2.6 Node.js工作流程
-
Node.js应用启动时,会开启JS线程(主线程)、由libuv提供的线程池和一个事件循环。JS线程负责执行应用代码,当发现有I/O操作时,直接提交给libuv的线程池并注册回调函数,不会等待I/O结束后再继续运行,而是拿到一个状态后继续执行,这就是“单线程非阻塞I/O”
-
I/O操作结束之后会有一个事件,该事件会放在事件队列中,事件循环每次都会检查是否有事件需要处理,如果有就处理,否则进行下一轮轮询;如果没有任何事件要处理则退出进程。这就是“事件驱动”
-
在I/O密集型应用中,主线程只负责提交任务,轮询结果,耗时的任务执行部分会提交给底层执行,这就是Node.js为什么会有如此高性能的原因。
2.7 Node.js API
- Node standard library
- 比较薄的API封装层
- 提供网络、文件、事件等操作,但实际是由底层来完成的
3 Node.js优缺点
3.1 优点
- 事件驱动、异步编程,在I/O密集场景下有着极高的性能
- 轻量高效,资源占用率低
- 使用JS作为应用层语言,语言门槛低
3.2 缺点
- 单进程,一旦JS线程出现未处理的错误,进程会退出,服务会终止
- 单线程(特指JS线程),一旦JS线程上出现耗时的CPU计算(加解密之类的操作),JS线程将出现阻塞,会拖慢事件轮询。
4 Node.js适用场景
- Restful API
- 实时WebSocket应用
- 前端工具链
- 桌面开发
Author gsemir
LastMod 2021-07-29