Node.js技术原理分析系列——Node.js调试能力分析

本文由体验技术团队屈金雄原创。

Node.js 是一个开源的、跨平台的 JavaScript 运行时环境,它允许开发者在服务器端运行 JavaScript 代码。Node.js 是基于 Chrome V8引擎构建的,专为高性能、高并发的网络应用而设计,广泛应用于构建服务器端应用程序、网络应用、命令行工具等。

本系列将分为9篇文章为大家介绍 Node.js 技术原理:从调试能力分析到内置模块新增,从性能分析工具 perf_hooks 的用法到 Chrome DevTools 的性能问题剖析,再到 ABI 稳定的理解、基于 V8 封装 JavaScript 运行时、模块加载方式探究、内置模块外置以及 Node.js addon 的全面解读等主题,每一篇都干货满满。

本文内容为本系列第1篇,以下为正文内容。

inspector 是什么

直接取官方文档中,对 inspector 的定义:

The node:inspector module provides an API for interacting with the V8 inspector.

翻译过来就是,inspector 模块提供了一组用于和 V8 inspector 交互的 API 。

解读:

  • node inspector 是 Node.js 内置模块
  • node inspector 仅提供与 V8 inspector 交互的能力,其本身并没有调试能力
  • Node.js 调试能力来自 V8 inspector

Node.js 调试原理

调试的目的是通过观察运行时数据来定位问题。Node.js 的运行时数据由 V8 引擎管理,为了实现调试功能,V8封装了一套 api 供外部查看运行时数据,这套 api 名字就是 V8 inspector(运行时是一个 websocket 服务)。V8 inspector 由于调试协议不同,不能直接与 Chrome DevTools 交互,于是 Node.js 提供了 inspector 模块,运行时也会启动一个 websocket 服务,用于适配。

在这里插入图片描述

如图所示,进入 Node.js 调试模式前,主线程需要创建一个 debugger server( websocket 服务,即时通讯服务,也即 node inspector ),用来实现 debugger client(例如 vscode 调试器或 Chrome DevTools ) 与 V8 inspector 通信,V8 inspector 再获取 Node.js 服务的数据,最终实现单步调试等功能。

经过封装与简化后,launch 模式启动调试时我们甚至感知不到 debugger server 了,但是它一定是存在的。

深入分析 – inspect 参数

分析过程中,我对相关源码做了粗读,除了源码本身,还参考了这篇文章:https://theanarkh.github.io/understand-nodejs/chapter24-Inspector/#11

本文使用的 Node.js 源码是 18.20.2

在这里插入图片描述

如上图所示,表示的是 Node.js 调试模式启动过程,大部分节点都是中文表述+函数名。

当我们用 node --inspect test.js 启动一个 js 脚本时,程序会启动 debugger server(一个 websocket 服务)。如上图所示,相关逻辑都在初始化 inspector 部分(蓝色节点),接下来细看一下这部分代码。

下图的起始节点 server.Start() 函数就是上图的末端节点 server.Start()。
在这里插入图片描述

图中每一个节点都对应一个函数。无需理解所有节点,我们重点关注着色的几个节点。

1.第一个蓝色节点

当我们运行 node --inspect test.js 命令,可以看到如下打印,打印的内容 Node.js 开发者一定都很熟悉。

Debugger listening on ws://127.0.0.1:9229/43b86c7c-e538-4d5c-98ba-3f5196d8e986
For help, see: https://nodejs.org/en/docs/inspector

// 这一行是Node开发者写的业务代码的打印
Server running at http://127.0.0.1:3000/

这个蓝色节点已经是启动代码执行的最后一步了,第一个橙色节点之后的部分在处理连接请求,也就是说,当代码走到第一个蓝色节点时,已经成功启动了一个 websocket 服务。

通过前面的代码还能看出,这个 websocket 服务在新起的子线程上运行,正因如此,调试程序才可以在主线程出现异常而崩溃的情况下,记录发送异常信息数据。

2.第一个橙色节点

注意这个节点代表一个回调函数,这个函数在服务启动时并没有执行。

它的执行是由 debugger client(例如 vscode 调试器或 Chrome DevTools )发起的 http 请求触发的,这次是client发起的第一次请求。

这次请求,对 vscode 调试器来说,就是它的 attach 模式( launch模式是把启动和连接操作合并了);对Chrome DevTools 来说,感觉上应该是通过轮询连接的,这个点暂时就不再深入研究了。

3.第二个橙色节点

client 紧接着会发第二次请求(未确认),请求头会携带 upgrade websocket 信息。这时会触发第二个橙色节点处的回调函数,当识别到是升级请求时,debugger server 才真正升级为 websocket 服务。

4.第二个蓝色节点

升级完成后,控制台会打印“Debugger attached.”,这也是我们调试时常见的控制台打印信息。

接下来,debugger server 就可以正常处理业务请求了。

5.特别关注一下红色节点

这里的代码就可以看出,debugger client 与 debugger server 建立连接的过程中,debugger server 与 V8 inspector 建立了连接。

其实整个初始化 inspector(启动 debugger server )的过程,是一套完整 websocket 实现,可以作为一个整体来看待。早期 Socket.io 模块是内置在 Node.js 中的。

– inspect-brk 参数

–inspect-brk 命令,可以在用户代码启动前中断,相当于在用户代码的第一行打了个断点。

如下图所示,我们用 node --inspect-brk test.js 命令启动服务。可以看到,只有 debugger server 启动成功的提示,没有 node 服务启动成功的提示。这是因为在执行用户代码前停住了。

这个命令在我们想要研究或调试 node 代码启动,又不知道研究对象启动入口位置时,比较有用

在这里插入图片描述

启动 debugger server 后,我们用连接上,这时可以看到执行停在了业务代码的第一行,而这一行我们并没有设置断点,如下图所示。
在这里插入图片描述

– inspect-wait 参数

这是 node 20 版本新增的启动参数,用于等待调试器连接后再执行代码。这样就可以从执行一开始就开始调试。

Node.js 三种常见的调试方式

本节的介绍,没有像其他网络教程那样手把手,step by step 地写清楚操作步骤,是因为有讲调试原理。

初学者理解本节的前提是先看懂调试原理。

一、vscode 调试

vscode 调试是 Node.js 开发者最推荐的调试方式,因为可以一键启动调试模式,可以不用像 Chrome DevTools 调试那样起额外的窗口。

vscode 调试 Node.js 分为 launch 和 attach 两种模式,这里先介绍一下 launch 模式。

launch 模式调试 Node.js

1.创建一个 Node.js 服务,就是 test.js 文件,内容如下:

const { createServer } = require('node:http');

const hostname = '127.0.0.1';
const port = 3000;

const server = createServer((req, res) => {
res.statusCode = 200;
res.setHeader('Content-Type', 'text/plain');
  res.end('Hello World');
});

server.listen(port, hostname, async () => {
  console.log(`Server running at http://${hostname}:${port}/`);
});

2.在 Node.js 服务入口文件所在的目录,点击 create a launch.json file 按钮,然后选择 Node.js 选项(这是选择调试器),vscode 会自动创建一个 launch.json 文件。

在这里插入图片描述

launch.json 文件内容如下,本场景就直接用自动生成的不需要作任何改动。

configurations 下的 request 字段表示的就是我们前面提到的调试模式,默认使用 launch 模式;name 表示这一套配置的名称,默认名称是 Launch Program,下一步会用到该名称;program 表示项目启动的入口文件。更多调试配置参考 vscode官网

{
  // Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
    {
      "type": "node",
      "request": "launch",
      "name": "Launch Program",
      "skipFiles": [
        // "<node_internals>/**"
      ],
      "program": "${workspaceFolder}\test.js"
    }
  ]
}

如果用如上文件,屏蔽12行,设置了不跳过内部代码,还可以调试到Node.js的JS源码。

  1. 如下图所示,打断点,并选择 Launch Program,然后点击绿色三角,启动调试。

在这里插入图片描述

attach 模式调试 Node.js

顾名思义,这个模式不会触发 Node.js 和 debugger server 的启动,只会作为 debugger client 附加到已经启动的 Node.js 服务和debugger server 上。如果能理解前面讲的调试原理,这里就很好理解了。

1.首先我们要运行 node --inspect test.js,这是在启动 debugger server。

同时启动的还有 node 服务,也就是图中的 http://127.0.0.1:3000。

在这里插入图片描述

2.在 launch.json 文件中添加 attach 模式配置

port 字段表示要连接到的 debugger server 的端口号,也就是我们上一步的 9229。

address 字段表示 debugger server 的地址,也就是上一步的 ws://127.0.0.1;debugger server 在本地的话,可以省略该配置。

{
  // Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
    {
      "type": "node",
      "request": "launch",
      "name": "Launch Program",
      "skipFiles": [
        "<node_internals>/**"
      ],
        "program": "${workspaceFolder}\test.js"
      },
    {
      "type": "node",
      "request": "attach",
      "name": "Attach Program",
      "port": 9229,
      // "address": "localhost",
    },
  ]
}

3.上一步添加完 Attach Program 配置后,多了一个启动选项,如下图所示。选择 Attach Program,然后点击绿色三角按钮,启动调试,即可连接上第一步启动的 debugger server。

在这里插入图片描述

远程调试

attach模式本地调试不常用,通常用于远程调试。目前vscode官方提供了两种远程调试方案:

  • 使用远程开发扩展包(官方推荐)

这个方案不仅能远程调试,还能用于远程开发,比如我们有时可能需要在本地Windows机器上编辑远程的Linux服务器上的项目。但是要安装一系列插件,有些复杂

  • attach模式连接远程 debugger server

在前文的attach模式配置中,加一个address字段,填上远程debugger server的IP地址即可。

二、Chrome DevTools 调试

当我们理解了 Node.js 调试原理,Chrome DevTools 调试就变得手到擒来。

1.首先我们还是要运行 node --inspect test.js,这是在启动 debugger server。

同时启动的还有 node 服务,也就是图中的 http://127.0.0.1:3000

在这里插入图片描述

2.接下来再打开 chrome 控制台,就能看见 Node.js 的图标。注意没有启动 debugger server 的时候,图标是不会出现的。点击它,就可以打开 Chrome DevTools。

在这里插入图片描述

3.首次打开 DevTools 时,需要在 Connection tab 页配置准备连接的 debugger server。

可以看到 DevTools 自带了两个地址,如果是 debugger server 是在本地启动,并且使用的是默认的 9229 端口,就不需要添加连接地址了。

在这里插入图片描述

4.DevTools 连接 debugger server

之前有过介绍,vscode 连接 debugger server 需要用 attach 模式启动调试。

DevTools 这边在完成配置后,不需要任何操作就能自动连上 debugger server。(大概)是因为 DevTools 会轮询所配置的地址。

连接成功后,会触发node服务打印“Debugger attached.”提示语。

在这里插入图片描述

5.开始调试

我们切到 Sources tab 页,发现已经有了要调试的代码。在需要的位置打断点,再触发断点,即可调试。

例如下图中,断点位置的代码是请求处理代码,我们只需要访问一下 http://127.0.0.1:3000 这个地址,即可触发断点。

在这里插入图片描述

三、命令行调试

命令行调试是没有客户端之前的方式,现在一般不用,但是如果需要在不方便使用前面介绍的两种方式的情况下,例如需要在服务器本地调试,可以用命令行调试。

我们把命令的双横杠去掉,也就是运行 node inspect test.js 命令,结果如下图所示:

我们进入到了Node.js的命令行调试模式,具体的用法参考官方文档的 Debug部分。

在这里插入图片描述

接下来贴一些常用命令用法:

步进#
  • cont, c:继续执行
  • next, n:下一步
  • step,s: 进入方法内部
  • out, o:退出当前方法
  • pause:暂停正在运行的代码(类似开发者工具中的暂停按钮)
断点#
  • setBreakpoint(), sb():在当前行设置断点
  • setBreakpoint(line), sb(line):在特定行设置断点
  • setBreakpoint(‘fn()’), sb(…):在函数主体的第一个语句上设置断点
  • setBreakpoint(‘script.js’, 1), sb(…):在第一行设置断点 script.js
  • setBreakpoint(‘script.js’, 1, ‘num < 4’), sb(…):在第一行设置条件断点script.js,仅当num < 4 计算结果为true
  • clearBreakpoint(‘script.js’, 1), cb(…):清除script.js 第一行的断点
信息#
  • backtrace, bt:打印当前调用栈

  • list(5):列出脚本源代码以及 5 行上下文(前后 5 行)

  • watch(expr):将表达式添加到观察列表,注意表达式需用引号括起来,如watch(“test2”)

  • unwatch(expr):从观察列表中删除表达式

  • unwatch(index):从观察列表中删除特定索引处的表达式

  • watchers:列出所有观察者及其值(每个断点上自动列出)

  • repl:打开调试器的 repl,在调试脚本的上下文中查看数据或表达式

  • exec expr, p expr:在调试脚本的上下文中执行表达式并打印其值

  • profile:启动 CPU profiling session

  • profileEnd:停止当前 CPU profiling session

  • profiles:列出所有已完成的 CPU profiling session

  • profiles[n].save(filepath = ‘node.cpuprofile’):将 CPU profiling session以 JSON 格式保存到磁盘

  • takeHeapSnapshot(filepath = ‘node.heapsnapshot’):获取堆快照并以 JSON 格式保存到磁盘

注意一下,repl命令可以进入断点所在上下文,方便地查看数据或表达式:

在这里插入图片描述

inspector 模块的 API

Node.js 的 inspector 模块提供了一组 API,用于在运行时与 V8 引擎进行交互,调试和分析 Node.js 应用程序。

这些 API 使开发者可以通过编程方式启动调试会话、设置断点、执行调试命令、收集性能数据等。

在这里插入图片描述

这里仅详细介绍两个方法:

1.inspector.Session 类的 post 方法可以向 v8 inspector 发送消息(指令),获取各种信息。v8 inspector 能识别的指令可以在 Chrome DevTools Protocol 中查看

贴一份官网的示例,对该类的能力可见一斑。

import { Session } from'node:inspector/promises';
import fs from'node:fs';
const session = newSession();
session.connect();

await session.post('Profiler.enable');
await session.post('Profiler.start');
// Invoke business logic under measurement here...

// some time later...
const { profile } = await session.post('Profiler.stop');

// Write profile to disk, upload, etc.
fs.writeFileSync('./profile.cpuprofile', JSON.stringify(profile));

2.inspector.open方法可以在节点启动后,以编程方式启动debugger server。

vscode有个通过进程id,attach到没有用–inspect模式启动的node服务的能力,大概就是通过该接口实现的,暂未确认。

下一节,我们将讲解如何在Node.js中新增一个内置模块,请大家持续关注本系列内容~学习完本系列,你将获得:

  • 提升调试与性能优化能力
  • 深入理解模块化与扩展机制
  • 探索底层技术与定制化能力

关于OpenTiny

欢迎加入 OpenTiny 开源社区。添加微信小助手:opentiny-official 一起参与交流前端技术~
OpenTiny 官网:https://opentiny.design/
OpenTiny 代码仓库:https://github.com/opentiny/
TinyVue 源码:https://github.com/opentiny/tiny-vue
TinyEngine 源码: https://github.com/opentiny/tiny-engine
欢迎进入代码仓库 Star🌟TinyEngine、TinyVue、TinyNG、TinyCLI~ 如果你也想要共建,可以进入代码仓库,找到 good first issue标签,一起参与开源贡献~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/970259.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

LLaMA-Factory DeepSeek-R1 模型 微调基础教程

LLaMA-Factory 模型 微调基础教程 LLaMA-FactoryLLaMA-Factory 下载 AnacondaAnaconda 环境创建软硬件依赖 详情LLaMA-Factory 依赖安装CUDA 安装量化 BitsAndBytes 安装可视化微调启动 数据集准备所需工具下载使用教程所需数据合并数据集预处理 DeepSeek-R1 可视化微调数据集处…

Spring Boot实战:拦截器

一.拦截器快速入门 1.1了解拦截器 什么是拦截器&#xff1a; 概念 &#xff1a;拦截器是Spring框架提供的核功能之, 主要来拦截的请求, 在指定法前后, 根据业务需要执预先设定的代码。 也就是说, 允许开发员提前预定义些逻辑, 在的请求响应前后执. 也可以在请求前阻其执. …

LabVIEW 用户界面设计基础原则

在设计LabVIEW VI的用户界面时&#xff0c;前面板的外观和布局至关重要。良好的设计不仅提升用户体验&#xff0c;还能提升界面的易用性和可操作性。以下是设计用户界面时的一些关键要点&#xff1a; 1. 前面板设计原则 交互性&#xff1a;组合相关的输入控件和显示控件&#x…

qt-C++笔记之QGraphicsScene和 QGraphicsView中setScene、通过scene得到view、通过view得scene

qt-C++笔记之QGraphicsScene和 QGraphicsView中setScene、通过scene得到view、通过view得scene code review! 文章目录 qt-C++笔记之QGraphicsScene和 QGraphicsView中setScene、通过scene得到view、通过view得scene1.`setScene` 方法2.通过 `scene` 获取它的视图 (`views()`)…

CI/CD部署打包方法

项目目前部署方式&#xff1a; 各地区服务器打包同一个runner&#xff08;需要互相排队&#xff0c;不并发&#xff09;各地区客户端可以并发打包&#xff0c;同个地区客户端打多个包需要排队 部署方法 下载gitlab-runner&#xff1a; https://docs.gitlab.com/runner/insta…

【含文档+源码】基于Web的在线课堂测试课程考评系统的开发与实现

项目介绍 本课程演示的是一款 基于Web的在线课堂测试课程考评系统的开发与实现&#xff0c;主要针对计算机相关专业的正在做毕设的学生与需要项目实战练习的 Java 学习者。 1.包含&#xff1a;项目源码、项目文档、数据库脚本、软件工具等所有资料 2.带你从零开始部署运行本套…

【vscode】VScode Remote SSH配置

VScode使用remote ssh 到服务器上的Docker容器中 1. 配置远程服务器docker容器的端口映射&#xff0c;例如将服务器的2222端口映射到container的22端口(默认) 1.1 在容器系统的sshd_config文件中配置参数 #配置文件 vim /etc/ssh/sshd_config #打开端口号 Port 221.2 建立容…

2月第九讲“探秘Transformer系列”

0.1 流程 使用Transformer来进行文本生成其实就是用模型来预测下一个词&#xff0c;完整流程包括多个阶段&#xff0c;如分词、向量化、计算注意力和采样&#xff0c;具体运作流程如下&#xff1a; 分词&#xff08;tokenize&#xff09;。把用户的输入文本&#xff08;此处假…

crewai框架(0.83.0)添加知识源

官方的文档如下 https://docs.crewai.com/concepts/knowledge但是不知道为什么&#xff0c;可能是版本的问题&#xff08;我用的是0.86.0&#xff09;&#xff0c;参考官方文档的配置我会报错&#xff0c;并且也导入不了数据库&#xff0c;也可能用的不是官方API。本文以常用的…

deepseek + embeding模型搭建本地知识库

上一篇文章讲了ollamadeepseek模型的本地化部署&#xff0c;具体能部署哪一款取决于你的土豪程度&#xff1a; 今天的目标是本地安装部署embeding模型&#xff0c;实现LLMembeding模型的rag知识库的本地化部署&#xff0c;包括&#xff1a; embeding模型的本地化部署anyhingL…

2、树莓派5第一次开机三种方式:使用外设 / 使用网线 / 使用wifi

本文整理了树莓派第一次开机方式&#xff0c;供大家参考 方式一&#xff1a;连接鼠标、键盘、显示器外设开机 树莓派自带USB接口及HDMI接口&#xff0c;因此可以通过USB连接鼠标键盘&#xff0c;HDMI接入显示器&#xff0c;再进行电源供电&#xff0c;就可以完成第一次开机 …

案例-02.部门管理-查询

一.查询部门-需求 二.查询部门-思路 API接口文档 三.代码实现 1.controller层&#xff1a;负责与前端进行交互&#xff0c;接收前端所发来的请求 注&#xff1a;Slf4j用于记录日志使用&#xff0c;可以省略private static Logger log LoggerFactory.getLogger(DeptControlle…

小程序包体积优化指南:静态资源条件编译与分包编译技巧

在开发小程序时&#xff0c;可能大家都遇到过包体积超限的情况&#xff0c;这对多平台支持、用户体验和加载速度带来不少困扰。UniApp 提供了一些强大的功能&#xff0c;比如静态资源的条件编译和分包编译&#xff0c;这些功能可以帮助我们减少小程序的包体积&#xff0c;提高加…

12. QT控件:多元素控件

0. 概述 Qt中提供的多元素控件 QListWidget QListView QTableWidget QTableView QTreeWidget QTreeView xxWidget 和 xxView的区别 以QTableWidget 和 QTableView 为例&#xff1a; QTableView 是基于MVC设计的控件&#xff0c;QTableView自身不持有数据。使用QTableView需…

CAS单点登录(第7版)20.用户界面

如有疑问&#xff0c;请看视频&#xff1a;CAS单点登录&#xff08;第7版&#xff09; 用户界面 概述 概述 对 CAS 用户界面 &#xff08;UI&#xff09; 进行品牌化涉及编辑 CSS 样式表以及一小部分相对简单的 HTML 包含文件&#xff0c;也称为视图。&#xff08;可选&…

android 的抓包工具

charles 抓包工具 官网地址 nullCharles Web Debugging Proxy - Official Sitehttps://www.charlesproxy.com/使用手册一定记得看官网 SSL Certificates • Charles Web Debugging Proxy http请求&#xff1a; 1.启动代理&#xff1a; 2.设置设备端口 3.手机连接当前代理 …

关于视频去水印的一点尝试

一. 视频去水印的几种方法 1. 使用ffmpeg delogo滤镜 delogo 滤镜的原理是通过插值算法&#xff0c;用水印周围的像素填充水印的位置。 示例&#xff1a; ffmpeg -i input.mp4 -filter_complex "[0:v]delogox420:y920:w1070:h60" output.mp4 该命令表示通过滤镜…

预测技术在美团弹性伸缩场景的探索与应用

管理企业大规模服务的弹性伸缩场景中&#xff0c;往往会面临着两个挑战&#xff1a;第一个挑战是精准的负载预测&#xff0c;由于应用实例的启动需要一定预热时间&#xff0c;被动响应式伸缩会在一段时间内影响服务质量&#xff1b;第二个挑战是高效的资源分配&#xff0c;即在…

【含开题报告+文档+PPT+源码】基于Spring+Vue的拾光印记婚纱影楼管理系统

开题报告 本论文旨在探讨基于Spring和Vue框架的拾光印记婚纱影楼管理系统的设计与实现。该系统集成了用户注册登录、个人资料修改、婚庆资讯浏览、婚庆套餐查看、婚纱拍摄预约、婚纱浏览与租赁、客片查看以及在线客服等多项功能&#xff0c;为用户提供了一站式的婚纱影楼服务体…

ASP.NET Core 使用 FileStream 将 FileResult 文件发送到浏览器后删除该文件

FileStream 在向浏览器发送文件时节省了服务器内存和资源&#xff0c;但如果需要删除文件怎么办&#xff1f;本文介绍如何在发送文件后删除文件&#xff1b;用 C# 编写。 另请参阅&#xff1a;位图创建和下载 使用FileStream向浏览器发送数据效率更高&#xff0c;因为文件是从…