惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

H
Hackread – Cybersecurity News, Data Breaches, AI and More
W
WeLiveSecurity
C
Check Point Blog
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
V
Vulnerabilities – Threatpost
GbyAI
GbyAI
A
Arctic Wolf
NISL@THU
NISL@THU
N
Netflix TechBlog - Medium
The Register - Security
The Register - Security
M
MIT News - Artificial intelligence
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
Microsoft Security Blog
Microsoft Security Blog
Cyberwarzone
Cyberwarzone
C
CERT Recently Published Vulnerability Notes
T
Tenable Blog
G
GRAHAM CLULEY
O
OpenAI News
S
Schneier on Security
Google Online Security Blog
Google Online Security Blog
Vercel News
Vercel News
宝玉的分享
宝玉的分享
Attack and Defense Labs
Attack and Defense Labs
T
The Blog of Author Tim Ferriss
量子位
aimingoo的专栏
aimingoo的专栏
The Cloudflare Blog
P
Privacy & Cybersecurity Law Blog
S
SegmentFault 最新的问题
MongoDB | Blog
MongoDB | Blog
Apple Machine Learning Research
Apple Machine Learning Research
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
L
LINUX DO - 热门话题
博客园_首页
F
Full Disclosure
Recent Commits to openclaw:main
Recent Commits to openclaw:main
D
Docker
U
Unit 42
A
About on SuperTechFans
博客园 - 司徒正美
Hacker News - Newest:
Hacker News - Newest: "LLM"
人人都是产品经理
人人都是产品经理
Application and Cybersecurity Blog
Application and Cybersecurity Blog
G
Google Developers Blog
Security Archives - TechRepublic
Security Archives - TechRepublic
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
J
Java Code Geeks
云风的 BLOG
云风的 BLOG
Scott Helme
Scott Helme
TaoSecurity Blog
TaoSecurity Blog

时间的朋友

Windows 命令行密码重置 Anaconda安装 typescript 注解解读1 Konvajs Shape加载自定义图片 sshpass 使用 webgl笔记 SharedArrayBuffer is not defined blender 常用快捷键 vue -- v3.4commit提交记录2 vue2 升级vue3报错问题整理 着色器 expressjs 源码 hyper-V arch linux 网络配置 element input数字格式化 three 拼接货架 WebAudio笔记 Windows nginx重启bat脚本 vue -- v3.4commit提交记录 URI malformed vue3 -- Class 对象在组件中使用范例 | 时间的朋友 ruby 安装和升级 element-plus 老版本cascader使用卡死问题 vue3 内置Transition组件 前端memo的实现 Vue -- vue-class-component源码 linux 优化脚本 typescript 装饰器 microbundle 源码 WSL2问题解决WslRegisterDistribution failed with error: 0x800701bc vue -- vue3利用createVNode函数,建立命令式调用组件 SSH connection failed: connect ECONNREFUSED 请求中获取浏览器推荐语言 flutter 生命周期 双向链表 堆和栈 EventSource 单向链表 简易的事件监听EventBus fork的仓库更新分支 forwardRef 定义的组件添加静态属性 js 获取滚动元素 canvas requestAnimationFrame画一个clock mousedown event中保持input的focus状态 vue -- @vue/compiler-core整体逻辑 IOS端h5 fixed滚动问题 vue -- compile结果代码解读 vue -- transformElement源码 Vue -- 内置指令源码 vue3 -- @vue/compiler-sfc compileScript源码 浏览器工作原理 对比2个版本号的方法 vite 向entry html中注入代码 小程序 -- 内部使用webview绑定微信公众号openId github coding同步action 小程序 -- 微信外部浏览器或者链接打开方式整理 MSE -- MediaSource 的前端使用 rollup 打包vue2组件 常用工具函数整理 Benchmark.js 使用 import-html-entry js沙箱实现源码 import-html-entry 笔记 axios core源码 国内开源镜像网站 nuxt -- docker-compose进行部署 vue -- provide和inject原理 vue -- hoistStatic原理 jenkins 配置模板代码 vue -- compileTemplate原理 小程序开发问题整理 webpack-dev-server proxy代理模块 vite 代理中更改请求头问题 Async.js flutter 问题整理 比较两个数组的不同项 ms源码解析 浏览器同源策略 前端缓存笔记 webpack5 schema-utils tsc 和 babel 编译typescript区别 typescript 学习笔记 esbuild api整理 Webpack Chain 源码 deepmerge库源码解读 Vue 自定义指令的执行机制 linux 问题整理 webpack5学习笔记 elementui form多表单验证 Webpack5 -- Assest Module vue -- 实现MenuTree组件 Vue -- VFor的编译处理 vue -- 使用中问题整理 算法基础知识 浏览器环境检测函数整理 yarn 问题搜集 c++笔记 webpack 模块加载原理 webpack 模块加载原理(二) Zero Width Characters slate.js API整理 slate.js 富文本编辑器
why-is-node-running
2024-07-20 · via 时间的朋友

why-is-node-running 为什么一个Node.js进程没有退出 🔗

why-is-node-running (Github) 用于诊断为什么一个Node.js进程没有退出。当你运行一个Node.js应用并且没有及时退出时,这个工具可以找出原因。

demo.js

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
function startServer () {
  const server = createServer()
  setInterval(() => {}, 1000)
  server.listen(0)
}

startServer()
startServer()

// logs out active handles that are keeping node running
setImmediate(() => whyIsNodeRunning())

结果输出

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
$ node example.js 
TCPSERVERWRAP created
TCPSERVERWRAP created
There are 6 handle(s) keeping the process running.

# Timeout
example.js:6  - setInterval(() => {}, 1000)
example.js:10 - startServer()

# TTYWRAP
index.js:20 - console.info(`TCPSERVERWRAP created`);

# TTYWRAP
(unknown stack trace)

# TCPSERVERWRAP
example.js:7  - server.listen(0)
example.js:10 - startServer()

# Timeout
example.js:6  - setInterval(() => {}, 1000)
example.js:11 - startServer()

# TCPSERVERWRAP
example.js:7  - server.listen(0)
example.js:11 - startServer()

安装 🔗

1
npm i -D why-is-node-running

使用 🔗

命令行使用 🔗

1
why-is-node-running jsfile.js

或者使用node的--import属性提前预加载

1
node --import why-is-node-running/include /path/to/some/file.js

源码 🔗

createHook 🔗

async_hooks是Node.js提供的一种机制,用于监控异步资源的生命周期,在异步操作开始和结束时执行回调,这在调试、性能分析和资源管理等方面非常有用。

async_hooks通过统一的接口,使开发者能够注册回调函数,这些回调函数会在异步操作的不同阶段被调用,如初始化(init)、前向(before)、后向(after)、承诺解析(promiseResolve)和销毁(destroy)。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
const async_hooks = require('async_hooks');

const hook = async_hooks.createHook({
  init(asyncId, type, triggerAsyncId, resource) {
    console.log(`Async operation started: ${type}`);
  },
  before(asyncId) {
    console.log(`Before async operation with id: ${asyncId}`);
  },
  after(asyncId) {
    console.log(`After async operation with id: ${asyncId}`);
  },
  destroy(asyncId) {
    console.log(`Async operation destroyed: ${asyncId}`);
  }
});

// 启用hook
hook.enable();

当异步操作发生时,回调会被调用,提供有关操作的信息,如类型、触发它的异步ID以及资源本身

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
init (asyncId, type, triggerAsyncId, resource) {
    if (IGNORED_TYPES.includes(type)) {
      return
    }
  
    const stacks = captureStackTraces().slice(1)
    asyncResources.set(asyncId, {
      type,
      resource,
      stacks
    })
},
destroy (asyncId) {
    asyncResources.delete(asyncId)
}

源码中忽略了以下类型,通过一个Map搜集堆栈信息

1
2
3
4
5
6
const IGNORED_TYPES = [
  'TIMERWRAP',
  'PROMISE',
  'PerformanceObserver',
  'RANDOMBYTESREQUEST'
]

这里重写了获取堆栈信息的方法,通过v8内置方法代理把堆栈对象输出 用于捕获当前JavaScript执行环境的调用栈轨迹。它通过暂时替换Error.prepareStackTrace方法,使用Error.captureStackTrace方法为目标对象target生成调用栈轨迹,最后恢复原先的Error.prepareStackTrace方法。返回值是捕获到的调用栈轨迹。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
// See: https://v8.dev/docs/stack-trace-api
function captureStackTraces () {
  const target = {}
  const original = Error.prepareStackTrace

  Error.prepareStackTrace = (error, stackTraces) => stackTraces
  Error.captureStackTrace(target, captureStackTraces)

  const capturedTraces = target.stack
  Error.prepareStackTrace = original
  return capturedTraces
}
  • 运行之前先关闭
  • 打印堆栈信息
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
function whyIsNodeRunning (logger = console) {
  hook.disable()

  const activeAsyncResources = Array.from(asyncResources.values())
    .filter(({ resource }) => resource.hasRef?.() ?? true)

  logger.error(`There are ${activeAsyncResources.length} handle(s) keeping the process running.`)

  for (const asyncResource of activeAsyncResources) {
    printStacks(asyncResource, logger)
  }
}