Arquitectura técnica de Node.js

¿Cómo llama JS a C++?

Qué no es Node.js

no es un marco web

  • Node.js no es un marco de back-end web
  • Entonces no se puede comparar Node.js con Flask o Spring

no es un lenguaje de programación

  • Node.js no es el JS del backend
  • Entonces no puedes comparar Node.js con Python o PHP

¿Qué es Node.js?

es una plataforma

  • Combina múltiples tecnologías.
  • Deje que JavaScript también llame a la interfaz del sistema y desarrolle aplicaciones de back-end

Node.js combina muchas tecnologías

  • motor V8
  • libuv
  • c-ares(域名解析)、http-parser(解析http)、OpenSSL(做https)、zlib(做加密)Bibliotecas implementadas por C/C++

Arquitectura técnica de Node.js

image.png

Como la versión de Node.js se ha actualizado de 0.8 a 12.11.1, su arquitectura también está cambiando.

Si desea consultar el código fuente, se recomienda consultar la versión 0.10. Puede buscar nodejs en github y elegir la versión 0.10.

github.com/nodejs/node…

Porque esta versión se ha utilizado durante mucho tiempo y el código fuente es mucho menor que la nueva versión.

Si quieres saber más, puedes visitar github.com/yjhjstz/dee…

La mayoría de las veces, solo necesitamos aprender la biblioteca estándar de Node.js

Para otros módulos, es suficiente tener una comprensión general

Cuando esté interesado en la implementación interna de Node.js, obtenga información sobre otras cosas además de la biblioteca estándar.

Pero puede que tengas que escribir bien C/C++

que son los enlaces

fondo

  • C/C++ implementa una biblioteca http_parser, que es muy eficiente
  • Solo podemos escribir JS, pero queremos llamar a esta biblioteca
  • Las llamadas directas definitivamente no tienen éxito, necesitamos un puente intermedio

ataduras

  • Node.js usa C++ para encapsular http_parser para que cumpla con ciertos requisitos (por ejemplo: su tipo de datos y el tipo de datos JS se encapsulan uno por uno), y el archivo encapsulado se llama http_parser_bindings.cpp (código C++)
  • Compílelo en un archivo .node con la herramienta de compilación proporcionada por Node.js
  • El código JS puede requerir directamente este archivo .node
  • De esta manera, JS puede llamar a la biblioteca de C++ y el puente en el medio es vinculante (http_parser_bindings.cpp)
  • Dado que Node.js proporciona muchos enlaces, agregue un s
  • esto es ataduras

Interacción entre JS y C++

查看: nodejs.cn/api/addons.…

插件通常会暴露可以从 Node.js 中运行的 JavaScript 访问的对象和函数。 当从 JavaScript 调用函数时,输入参数和返回值必须映射到 C/C++ 代码和从 C/C++ 代码映射。

image.png

在test.js就实现了通过JS引入addon这样的node.js文件,调用了addon.cc(C/C++)中的add方法

C++ 调用JS回调

查看: nodejs.cn/api/addons.…

插件中的常见做法是将 JavaScript 函数传给 C++ 函数并从那里执行它们

image.png

args[0]就是JS中的那个函数,但是args[0]不能直接给C++,C++是看不懂JS函数的,Node.js提供了Cast的工具,它可以把JS的函数转化为C++能看懂的函数cb。JS中的函数addon就会被C++调用,调用的时候传了参数hello world(C++中构造了JS能看懂的string) ,于是就输出了hello world

只要能让JS调用C++的代码,JS的能力就会被无限扩大

Node.js提供的binding我们可以使用,如果现在有一个新的C++库,Node.js没有把它绑定进来怎么办?

Node.js还提供另外一个功能: 允许你自己写C++,把C++库绑定到JS上,JS就可以调用C++库了,这就是Node.js的另外一个能力,C/C++插件(自定义其他能力)

Node.js的依赖

image.png

在Node.js V0.10 版本中有7个依赖,我们主要需要掌握两个依赖v8和uv(也就是libuv),其他的优先级往后放

  • cares:DNS
  • http_parser: http
  • npm: 包管理器
  • openssl: https
  • zlib: 压缩

libuv & v8 的功能

libuv是什么

背景

  • FreeBSD 系统上有kqueue
  • Linux系统上有epoll
  • Windows 系统上有IOCP
  • Ryan为了实现一个跨平台的异步I/O(所有的输入和输出,如:写文件,访问网络,连接打印机打印文件即系统和外界进行的所有交互)库,开始写libuv
  • libuv会根据系统自动选择合适的方案(kqueue/epoll/IOCP)

功能

  • 可以用于TCP(http是基于TCP/IP,只要能操作TCP就可以做http服务器)/UDP(QQ聊天)/DNS(baidu.com对应的IP)/文件(文件的读取)等的异步操作
  • 有了这些功能Node.js就可以摆脱IO瓶颈了,涉及到IO的操作交给C语言去做,JS只负责简单的调用

v8是什么

功能

  • 将JS源代码变成本地代码(01)并执行
  • 维护调用栈,确保JS函数的执行顺序
  • 内存管理,为所有对象分配内存
  • 垃圾回收,重复利用无用的内存(垃圾回收的目的是为了再次重复利用,因为内存是有限的,比如:你用完了2k的内存,用完了就得还给我,还要给下一程序使用呢!)
  • 实现JS的标准库(如:数组的sort、数组的splice等函数)
  • v8本身是多线程的

注意

  • V8不提供DOM API(如:在V8中不能操作douemnt.createElement()...,它是浏览器提供的)
  • V8执行JS是单线程的(V8本身是多线程的)
  • 可以开启两个线程分别执行JS(并不是真正意义上的多线程,这两个线程之间毫无瓜葛)
  • V8本身是包含多个线程的,如:垃圾回收为单独线程
  • 自带 eventloop 但 Node.js并没有使用自带的eventloop而是基于libuv自己做了一个eventloop

eventloop

Event Loop 是什么

什么是Event

  • 计时器到了就会产生一个事件,这个事件产生就会执行回调(内部的)
  • 文件可以读取了、读取出错了的时候是操作系统要单独生成一个事件告诉JS。如:拷贝文件到U盘上,如果发现此时已经有另外一个文件也在拷贝,那么此时第二个拷贝文件就会很慢,因为硬盘的读写速度是有极限的(外部的,文件再在硬盘上,而硬盘和操作系统是分开的)
  • socket有内容了、关闭了。如:用户请求了我们的服务器,socket有内容了操作系统得告诉JS要开始读用户的内容。socket关闭了,也得告诉JS(外部的,socket一般为另外一台机器传过来的)

什么是Loop

  • Loop 就是循环,比如: while(true) 循环
  • 由于事件是分优先级的,所以处理起来也是分先后的

举例:

三种不同的事件

setTimeout(f1,100)        // 计时器到时了
fs.readFile('/1.txt',f2)  // 文件可以读了
server.on('close',f3)     // 服务器关闭了

如果同时触发,Node会怎么办?执行f1?f2?f3

- 肯定会有某种顺序(优先级)
- 这种顺序应该是人为规定的(如果有读文件就先读文件 => 如果有请求就先处理请求=>如果有计时器就执行计时器 ====> 进入循环Loop => 如果有读文件就先读文件 => 如果有请求就先处理请求 => .......)
  • 所以Node.js需要按照顺序轮询每种事件
  • 这种轮询往往都是循环的,1->2->3->1->2->3

EventLoop

  • 操作系统可以触发事件,JS可以处理事件
  • Event Loop 就是对事件处理顺序的管理

Event Loop

顺序示意图

image.png

重点阶段

  • timers 检查计时器
  • poll 轮询阶段,处理大部分请求(如:读文件,http请求),检查系统事件
  • check 检查阶段,处理 setImmediate 回调
  • close callbacks 看下有没有sockert关闭的回调
  • 再回到timers 检查计时器。。。 如此循环下去
  • 如果Node.js发现没有什么事做就会停在poll(轮询)阶段,如果发现有事情做,如执行setImmediate就会进入下个阶段check(while循环,一直问操作系统有没有文件可以读、有没有网络请求可以处理、。。。。)。poll阶段是Node.js停留时间最长的、优先级最高的
  • 其他阶段用的较少

大部分时候timers中的定时器是后执行的(虽然它在最前面),因为定时器至于在大于或等于时间的时候才会被执行,如果时间没到就会执行timers下面的阶段,等其他阶段如poll阶段或者check阶段执行完了,计时器的时间到了或者超了再执行timers

一个面试题

请问下面的代码哪个先执行?

setTimeout(f1,0)
setImmediate(f2)

===》 不确定

(1) 大部分情况下是setImmediate先执行,因为大部分时候,Node.js都是停留在poll 阶段,这个时候如果要执行JS就会先经过check阶段,这个阶段就是处理setImmediate

(2) 有一个情况是例外的: Node.js第一次进来时会先看下有没有timers

注意

  • 大部分时间,Node.js都停留在poll轮询阶段(如果这个阶段比较耗时,那么timers就会自动往后推)
  • 大部分事件都在poll阶段被处理,如文件、网络请求

总结

  • 用libuv进行异步I/O操作(如:让libuv读文件,文件读完后Node.js接替后面的事情(Node.js可以处理文件了!!!)
  • 用event loop 管理不同事件处理顺序(先polls(操作系统) -> check -> close -> timers -> polls(操作系统) ... 如此循环) (Node.js可以管理事件的顺序了!!!)
  • 用C/C++ 库高效处理DNS/HTTP...(Node.js又可以处理网络了!!!)(因为Node.js是借助C++处理网络请求,因此处理网络请求速度很快)
  • 用bindings 让JS能和C/C++沟通(JS也可以调用C++的代码了!!!)(把C++代码编译成.node文件给JS去require,require的过程中JS传个函数给C++,C++调用JS)
  • 用V8运行用户写的JS(写好了JS代码在哪里运行JS代码呢?)
  • 用Node.js标准库进化JS代码(都让用户写JS,用户就不爽了,帮用户写好了一些东西,用户直接用)
  • 这就是Node.js

Node.js工作流程

image.png

  • Application就是我们写的JS
  • 我们写JS放到V8上运行
  • 运行的过程中发现JS中写了一个定时器
  • Node.js调用Node.js的bindings / API ,把定时器放到 Event Loop 中,告诉Event Loop 100毫秒之后要执行一个函数
  • Event Loop 中有很多不同的队列(如:timers、poll、check等),Event Loop 会等待恰当的时机去执行队列中的代码
  • 如果此时在poll阶段需要读一个文件,这时Event Loop 就会利用libuv开一个线程去读这个文件(JS不参与),读完文件之后,操作系统就会返回一个事件给Event Loop,Event Loop 发现文件读好了,就传回给V8,然后就传回到我们写代码的地方
  • 处理网络请求也一样libuv得到了用户传过来的网络请求,就会把它放到 Event Loop 中的poll阶段,poll阶段得到这个请求之后,就会调用JS代码把它在v8中执行,将结果返回到写diamagnetic的地方

整个过程我们发现:V8 和 libuv是最重要的,而Node.js中的bindings / API 就是V8 和 libuv 中间的桥梁,我们写的代码只是整个平台中很小的一部分,这就是Node.js的工作流程

我们写的代码 ==> V8上运行 ==> V8通过Node.js的bindings/API ==> 使用libuv提供的功能 / 其他的C++提供的功能完成用户所需要的功能

因此说Node.js不是一门语言,也不是一个框架,它就是一个平台!!!

Node.js API与学习思路

image.png

接下来我们只需要架构图中最上面的一块 Node.js API(官方提供的函数)

API文档

官方地址

这些文档更像是字典,实在是太多了,没有欲望通过阅读文档去学习

民间版本(推荐)

  • devdics.io: devdocs.io/
  • 进入之后开启Node.js 10 LTS
  • 搜索功能非常方便
  • 可开启主题
  • 可离线观看

image.png

API到底有哪些功能

重点关注黄色标注的API

image.png

  • Buffer: 一小段缓存(大文件一点一点上传)
  • Child Processes: 子进程(Node.js的分身)
  • Cluster: 集群 (把多个Node.js集合到一起,每个Node.js做不同的事情,它们之间的关系就是主要和次要的关系)
  • Debugger: 调试
  • Events: 对应的就是EventHub,发布订阅模式(一个对象提供了on、off、emit)
  • File System: 文件系统,操作文件/目录
  • Globals: 全局变量,如:__dirname:当前文件所在目录;__filename: 当前文件的文件名
  • Http
  • Path: 路径
  • Query Strings: 处理URL
  • Stream: 流格式的数据的处理
  • Timers: settimeout 、setInterval、setImmediate
  • URL
  • Worker Threads*

学习路线

基础 - Web - 框架

  • 先学基础,以任务为导向学习
  • 逐个学习文件、HTTP、Stream等模块
  • 在学Web、数据库、AJAX相关知识
  • 最后学习框架,以项目为导向
  • 以Express为切入点,制作完整网站

约定

  • 记笔记,写博客
  • CRM学习法贯穿整个学习过程(copy => run => modify,学习任何技术最快的方式)
  • 学习调试工具和思路

Supongo que te gusta

Origin juejin.im/post/7246583632482713660
Recomendado
Clasificación