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://inspect
inspect
访问服务器的开发工具
内存
选项卡,您可以在请求轰炸服务器之前、期间和之后拍摄快照并检查堆
node benchmark.js
开始用请求轰炸服务器。这是由autocannon
驱动的,您可能需要增加连接数
或持续时间
以查看差异
它应该不会被清理,因为您将依赖于该名称空间。它可以通过
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在一起