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

推荐订阅源

Stack Overflow Blog
Stack Overflow Blog
D
Darknet – Hacking Tools, Hacker News & Cyber Security
爱范儿
爱范儿
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
有赞技术团队
有赞技术团队
罗磊的独立博客
博客园 - 三生石上(FineUI控件)
小众软件
小众软件
L
LINUX DO - 最新话题
T
Troy Hunt's Blog
博客园_首页
量子位
Jina AI
Jina AI
S
SegmentFault 最新的问题
IT之家
IT之家
Hacker News - Newest:
Hacker News - Newest: "LLM"
大猫的无限游戏
大猫的无限游戏
N
News | PayPal Newsroom
P
Proofpoint News Feed
Cyberwarzone
Cyberwarzone
S
Securelist
Google Online Security Blog
Google Online Security Blog
P
Privacy International News Feed
博客园 - Franky
美团技术团队
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
NISL@THU
NISL@THU
C
Cisco Blogs
V
Vulnerabilities – Threatpost
腾讯CDC
The Hacker News
The Hacker News
K
Kaspersky official blog
C
Cyber Attacks, Cyber Crime and Cyber Security
雷峰网
雷峰网
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
Security Archives - TechRepublic
Security Archives - TechRepublic
A
About on SuperTechFans
Webroot Blog
Webroot Blog
The Register - Security
The Register - Security
Scott Helme
Scott Helme
B
Blog
Security Latest
Security Latest
Last Week in AI
Last Week in AI
Google DeepMind News
Google DeepMind News
W
WeLiveSecurity
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
T
Tenable Blog
Blog — PlanetScale
Blog — PlanetScale
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
S
Schneier on Security

时间的朋友

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)
  }
}