Node.js cls挂钩内存泄漏或错误使用?

Node.js cls挂钩内存泄漏或错误使用?,node.js,express,cls-hooked,Node.js,Express,Cls Hooked,以下代码会增加内存使用,直到崩溃: const httpContext = require('express-http-context'); async function t2() { } async function t1() { for (let i = 0; i < 100000000; i++) { httpContext.ns.run(t2); } } t1(); const httpContext=require('express-http-context'

以下代码会增加内存使用,直到崩溃:

const httpContext = require('express-http-context');
async function t2() {
}

async function t1() {
  for (let i = 0; i < 100000000; i++) {
    httpContext.ns.run(t2);
  }
}
t1();
const httpContext=require('express-http-context');
异步函数t2(){
}
异步函数t1(){
对于(设i=0;i<100000000;i++){
httpContext.ns.run(t2);
}
}
t1();
运行时使用:node--inspect--max old space size=300 ns

问题是:名称空间上下文映射从未清理

cls hooked/context.js中有一个函数destroy(id),但从未调用过

我还尝试了ns.bind、ns.runPromise(执行ns.exit())和ns.bind

运行完成后,如何删除上下文

守则:

const httpContext = require('express-http-context');
function t2() {
}

async function t1() {
  for (let i = 0; i < 100000000; i++) {
    httpContext.ns.run(t2);
  }
}
t1();
const httpContext = require('express-http-context');
async function t3() {
}
function t2() {
  t3();
}

async function t1() {
  for (let i = 0; i < 100000000; i++) {
    httpContext.ns.run(t2);
  }
}
t1();
const httpContext=require('express-http-context');
函数t2(){
}
异步函数t1(){
对于(设i=0;i<100000000;i++){
httpContext.ns.run(t2);
}
}
t1();
工作

守则:

const httpContext = require('express-http-context');
function t2() {
}

async function t1() {
  for (let i = 0; i < 100000000; i++) {
    httpContext.ns.run(t2);
  }
}
t1();
const httpContext = require('express-http-context');
async function t3() {
}
function t2() {
  t3();
}

async function t1() {
  for (let i = 0; i < 100000000; i++) {
    httpContext.ns.run(t2);
  }
}
t1();
const httpContext=require('express-http-context');
异步函数t3(){
}
函数t2(){
t3();
}
异步函数t1(){
对于(设i=0;i<100000000;i++){
httpContext.ns.run(t2);
}
}
t1();
内存再次泄漏

cls hooked async_hook方法init()将上下文添加到_contexts映射中。 cls hooked async_hook方法destroy()从_contexts映射中删除上下文

问题是,毁灭永远不会被调用


这是cls hooked中的一个bug,还是与当前的异步钩子不兼容?

正如OP所指出的,这种用法肯定是不正确的

OP应该只执行一次
ns.run()
,并且
run
中的所有内容都将属于相同的上下文

请看以下正确使用的示例:

var createNamespace = require('cls-hooked').createNamespace;
 
var writer = createNamespace('writer');
writer.run(function () {
  writer.set('value', 0);
 
  requestHandler();
});
 
function requestHandler() {
  writer.run(function(outer) {
    // writer.get('value') returns 0
    // outer.value is 0
    writer.set('value', 1);
    // writer.get('value') returns 1
    // outer.value is 1
    process.nextTick(function() {
      // writer.get('value') returns 1
      // outer.value is 1
      writer.run(function(inner) {
        // writer.get('value') returns 1
        // outer.value is 1
        // inner.value is 1
        writer.set('value', 2);
        // writer.get('value') returns 2
        // outer.value is 1
        // inner.value is 2
      });
    });
  });
 
  setTimeout(function() {
    // runs with the default context, because nested contexts have ended
    console.log(writer.get('value')); // prints 0
  }, 1000);
}
此外,
cls hooked
内部的实现确实表明上下文是通过异步钩子回调
destroy(asyncId)

destroy(asyncID)
在与
asyncID
对应的资源被销毁后调用。它也从embedder API emitDestroy()异步调用。一些
资源
依赖垃圾收集进行清理,因此如果引用传递给
init
的资源对象,则可能永远不会调用
destroy
,从而导致应用程序内存泄漏。如果资源不依赖于垃圾收集,那么这将不是问题。

这是我的repo,通过使用
autocannon

根据测试,堆的利用率几乎没有增加(正如我们处理HTTP请求时所预期的那样)

CLS钩子和异步钩子的内存利用率 目的 该存储库是一个小型测试,用于查看使用
cls hooked
async hook
Node.js
中传递上下文时内存的利用情况

用法
  • npm运行启动
    用于CLS挂钩服务器
    npm运行异步
    用于异步挂钩服务器

  • 转到Chrome并粘贴
    chrome://inspect

  • 单击
    inspect
    访问服务器的开发工具

  • 转到
    内存
    选项卡,您可以在请求轰炸服务器之前、期间和之后拍摄快照并检查

  • node benchmark.js
    开始用请求轰炸服务器。这是由
    autocannon
    驱动的,您可能需要增加
    连接数
    持续时间
    以查看差异

  • 结果 钩住 斯达 1% 2.5% 50% 97.5% 平均值 科技发展署 马克斯 请求/秒 839 839 871 897 870.74 14.23 839 字节/秒 237kB 237kB 246kB 253kB 246kB 4.01kB 237kB
    它应该不会被清理,因为您将依赖于该名称空间。它可以通过
    cls.destronNamespace(name)
    cls.reset()
    手动清除。当我使用express并行运行一个或多个http请求并且httpContext中间件连接到express服务器时,我仍然可以清除/重置命名空间吗?在异步中,我永远不知道http请求何时进行。此外,express http上下文中间件也只执行ns.run()。谁在清理express http上下文?!我不完全确定
    express http context
    如何实现
    cls hooked
    ,但是,在启动
    express
    之前,您应该创建一个名称空间,该名称空间将是保存和检索所有内容的上下文。它都是通过node.js处理的AsyncResource引用的(从技术上讲,您可以使用较新的节点版本通过AsyncLocalStorage实现)。对销毁事件的垃圾收集进行了一些讨论。也许你可以试着循环,看看当你继续在上下文中保存很多东西时是否有内存问题,看看当事件被破坏时它是否会被擦除。当前的测试只是创建一个新的
    上下文
    ,它有点像用于存储内容的对象。除非指定上下文,否则上下文永远不会被销毁。然而,上下文中的项可能会被删除(我需要确认这一点,稍后将进行一些测试)const nsid='a6a29a6f-6747-4b5f-b99f-07ee96e32f88';const ns=cls.createNamespace(nsid);/**Express.js中间件,负责初始化每个请求的上下文。*/函数中间件(req、res、next){ns.run(()=>next());}这就是ExpressHTTP上下文集成到expess服务器中的方式。不清除所以解决方案是反转任何httpContext.set()httpContext.set(x,未定义)调用以清除指向自己对象的链接,从而不通过悬挂上下文增加堆?使用express http context后,会对每个http请求执行ns.run()。_上下文映射不显示在异步或命名空间中,而是显示在对象中。在示例代码中,这些对象。对象之间的距离最多可达ns.run()个。和max old在一起