Node.js 是如何跑起来的

本文为来自 字节跳动-国际化电商-S 项目团队 成员的文章,已授权 ELab 发布 。
一个 TCP 连接的案例?TCP 服务端?const.NET = require('net');const server = new net.Server();server.listen(9999, '127.0.0.1', () => {console.log(`server is listening on ${server.address().address}:${server.address().port}`);});server.on('connection', (socket) => {server.getConnections((err, connections) => {console.log('current clients is: ', connections);});socket.on('data', (data) => {console.log(`received data: ${data.toString()}`);});});
?TCP 客户端?const net = require('net');const client = new net.Socket();client.connect({port: 9999,address: '127.0.0.1',});client.on('connect', () => {console.log('connect success');client.write(`Hello Server!, I'm ${Math.round(Math.random() * 100)}`);});疑问?
NodeJS 代码是如何跑起来的
TCP 连接在 NodeJS 中是如何保持一直监听而进程不中断的
NodeJS 是如何处理并发连接的,当遇到阻塞型调用时如何不阻塞主线程的
核心架构?NodeJS 架构?

Node.js 是如何跑起来的

文章插图
NodeJS 源码分为三层:JS、C++ 以及 C 。
JS 层JS 层提供面向用户的调用底层能力的接口,即各种 NodeJS 原生模块,如 net、http、fs、DNS 以及 path 等
C++ 层C++ 层主要通过 V8 为 JS 层提供与底层交互的能力,起到类似桥梁的作用,通过 V8 不仅实现 JS 的解释执行,还扩展的 JS 的能力边界
C 层C 层主要包括 Libuv 这一跨平台的异步 IO 库以及其他第三方 C 库
启动过程
Node.js 是如何跑起来的

文章插图
image.png
?分析?注册 C++ 模块RegisterBuiltinModules 函数的作用是注册一系列 C++ 模块,通过宏定义展开,最终变成如下逻辑:
void RegisterBuiltinModules() {_register_async_wrap();_register_buffer();_register_fs();_register_url();// ...}通过注册函数,将各个 C++ 模块维护在 modlist_internal 这一链表中,后续在原生 JS 模块中调用 C++ 模块时就可以根据模块名找到对应的模块 。
创建 Environment 对象Environment 在 NodeJS 中是一个运行时的环境对象,很多全局变量都托管在该类上,创建完 environment 后,就将其和 Context 进行绑定,后续 V8 可通过 context 获取 env 对象 。
下面简单介绍一下 V8 的 isolate 、 context、scope、handle 等对象 。
isolate 是一个独立隔离实例的环境,同一时刻只能被一个线程进入;
context 可以理解为执行上下文对象,可以导入不同的环境变量和函数;
Scope 指的是作用域,可看成是句柄的容器,一个作用域里面可以有很多个句柄;
HandleScope 是用来管理 Handle 的,而 Context::Scope 仅仅用来管理 Context 对象 。
Handle 是 V8 引用对象的技术手段,Handle 分为 Local 和 Persistent 两种 。Local 是局部的,它同时被 HandleScope 进行管理 。persistent,类似于全局的,不受 HandleScope 的管理,其作用域可以延伸到不同的函数 。
Node.js 是如何跑起来的

文章插图
初始化 loader 和执行上下文RunBootstrApping 主要调用了 BootstrapInternalLoaders 和 BootstrapNode 函数 。
BootstrapInternalLoaders 用于编译执行 /lib/internal/bootstrap/loader.js,它的具体逻辑是为了NodeJS 能在JS层 通过 binding 函数加载C++模块,以便在原生 JS 模块中调用 C++ 模块 。
BootstrapNode 用于初始化执行上下文,暴露 global 对象在全局上下文中,编译执行 /lib/internal/bootstrap/node,从而设置一些全局变量或方法到 global 或者 process
// lib/internal/bootstrap/node.js// proces 挂载一系列属性方法{process.dlopen = rawMethods.dlopen;process.uptime = rawMethods.uptime;// TODO(joyeecheung): either remove them or make them publicprocess._getActiveRequests = rawMethods._getActiveRequests;process._getActiveHandles = rawMethods._getActiveHandles;// TODO(joyeecheung): remove theseprocess.reallyExit = rawMethods.reallyExit;process._kill = rawMethods._kill;const wrapped = perThreadSetup.wrapProcessMethods(rawMethods);process._rawDebug = wrapped._rawDebug;process.hrtime = wrapped.hrtime;process.hrtime.bigint = wrapped.hrtimeBigInt;process.cpuUsage = wrapped.cpuUsage;process.resourceUsage = wrapped.resourceUsage;process.memoryUsage = wrapped.memoryUsage;process.kill = wrapped.kill;process.exit = wrapped.exit;process.openStdin = function() {process.stdin.resume();return process.stdin;};}// global 挂载一系列属性和方法if (!config.noBrowserGlobals) {// Override global console from the one provided by the VM// to the one implemented by Node.js// https://console.spec.whatwg.org/#console-namespaceexposeNamespace(global, 'console', createGlobalConsole(global.console));const { URL, URLSearchParams } = require('internal/url');// https://url.spec.whatwg.org/#urlexposeInterface(global, 'URL', URL);// https://url.spec.whatwg.org/#urlsearchparamsexposeInterface(global, 'URLSearchParams', URLSearchParams);const {TextEncoder, TextDecoder} = require('internal/encoding');// https://encoding.spec.whatwg.org/#textencoderexposeInterface(global, 'TextEncoder', TextEncoder);// https://encoding.spec.whatwg.org/#textdecoderexposeInterface(global, 'TextDecoder', TextDecoder);// https://html.spec.whatwg.org/multipage/webappapis.html#windoworworkerglobalscopeconst timers = require('timers');defineOperation(global, 'clearInterval', timers.clearInterval);defineOperation(global, 'clearTimeout', timers.clearTimeout);defineOperation(global, 'setInterval', timers.setInterval);defineOperation(global, 'setTimeout', timers.setTimeout);defineOperation(global, 'queueMicrotask', queueMicrotask);// Non-standard extensions:defineOperation(global, 'clearImmediate', timers.clearImmediate);defineOperation(global, 'setImmediate', timers.setImmediate);}// ...


推荐阅读