【问题标题】:MaxListenersExceededWarning: Possible EventEmitter memory leak deteMaxListenersExceededWarning:可能的 EventEmitter 内存泄漏检测
【发布时间】:2019-07-19 15:52:28
【问题描述】:

我已阅读有关此错误的信息并知道它是什么,并且还知道如何通过将MaxListeners 设置为0 来禁用它。但我想知道是什么导致了这个错误,所以我可以正确处理它。

基本上这里有一个机器人来检查我的帐户是否有新消息。我需要一次检查很多账户,所以我写了这个机器人来做这件事。我在这段代码中有 4 个函数:

1 - load_proxy -> reads list of proxies from a file and puts them into an array
2 - load_accounts -> reads list of accounts from a file and puts them into an array
3 - init-> opens a browser , in a while loop fetching accounts and calling the next function  
4 - open_tab-> opens a tab in the browser and checks the account and calles the next function to log the result 

5 - wite_to_file-> writing the result into a text file 


const puppeteer = require('puppeteer');
const fs = require('fs');
const PROXYSTACK = [] ;
const PROXYDB = [] ;
const ACCOUNTSTACK = [] ;
const ACCOUNTDB = [] ;
var   inprogress = false ;


setInterval(()=>load_proxy() , 5000 );
setInterval(()=>load_accounts() , 5000 );
setInterval(function(){

    if( !inprogress)
    {
        init();
    }

} , 2000 );


function load_proxy(){

    var lines = fs.readFileSync('./proxy.txt', 'utf-8')
        .split('\n');

        for(var i = 0 ; i< lines.length ; i++ )
        {
            let line  = lines[i];
            if(line == '' || typeof line == 'undefined')
            {
                continue ;
            }

            line = line.replace('\r' , '');
            if(PROXYDB.includes(line))
                continue ;

            PROXYSTACK.push(line);
            PROXYDB.push(line);

        }

}
function load_accounts(){

    var lines = fs.readFileSync('./accounts.txt', 'utf-8')
        .split('\n');

        for(var i = 0 ; i< lines.length ; i++ )
        {
            let line  = lines[i];
            if(line == '' || typeof line == 'undefined')
            {
                continue ;
            }

            line = line.replace('\r' , '');
            if(ACCOUNTDB.includes(line))
                continue ;

            ACCOUNTDB.push(line);
            ACCOUNTSTACK.push(line);

        }

}



async function init(){

    inprogress = true ;

    if(PROXYSTACK.length <= 0 )
    {
        console.log('========================================= > OUT OF PROXY !!!!');
        inprogress = false ;
        return ;
    }

    if(ACCOUNTSTACK.length <= 0 )
    {
        console.log('========================================= > OUT OF ACCOUNT !!!!');
        inprogress = false ;
        return ;
    }

    var ipport =  PROXYSTACK.pop().replace('\r' , '');
    console.log(` ----> current ${ipport} `);

    var  browser = await puppeteer.launch({headless: true        ,  args: ['--proxy-server=' + ipport  , '--no-sandbox', '--disable-setuid-sandbox' , ]});
    browser._process.once('close', () => {
        console.log(' ------------------------  closed !');
        inprogress = false;
    });
    browser.on('disconnected', () => {
        console.log(' ------------------------  disconnecte !');
        inprogress = false;
    });

    var  mainpage = await browser.newPage();
    await mainpage.setViewport({width: 1200, height: 1000});

    while( inprogress )
    {
        var line_number = ACCOUNTSTACK.length ;
        if(line_number == 0 )
        {
            inprogress = false ;
            break ;
        }
        var account = ACCOUNTSTACK.pop();
        console.log(account);


        var check = await open_tab(account , line_number , mainpage);
        if(check === 'fatalerror')
        {
            console.log('========================================= >  FATAL ERROR CHANGING IP ');
            try {
                await browser.close();
            }
            catch (e) {

            }

            inprogress = false ;
        }


    }


}


async function open_tab(account  , line_num , mainpage ) {

    console.log(`  ---- > checking  ${account} `);
    let link = `https://example.com`;



    try {


        let user_password = account.split(':');


        if(!await mainpage.$('#username'))
        {
            console.log('...loading login page');
            await mainpage.goto(link , {timeout: 0});
            console.log('...done');

            if(await mainpage.$('.fatalerror'))
            {
                ACCOUNTSTACK.push(account);
                await mainpage.screenshot({path:  './fatalerror-a-'+line_num+'.jpg'  });
                return 'fatalerror';
            }

            console.log('...waitnign for login filds');

            await Promise.race([
                mainpage.waitForSelector('#username'),
                mainpage.waitForSelector('.fatalerror'),
            ]).catch(function (error) {
                throw new Error(error);
            });
            if(await mainpage.$('.fatalerror'))
            {
                ACCOUNTSTACK.push(account);
                await mainpage.screenshot({path:  './fatalerror-b-'+line_num+'.jpg'  });
                return 'fatalerror';
            }
            console.log('...done');



        }


        console.log('...typing user password');

        await mainpage.$eval('#username', (el ) => el.value = '' );
        await mainpage.$eval('#password', (el ) => el.value = '' );

        await mainpage.type('#username', user_password[0], {delay: 10})
        await mainpage.type('#password', user_password[1], {delay: 10})
        console.log('...done');

        console.log('...clicking login button');
        await mainpage.click('button.primary-button')

        await Promise.race([
            mainpage.waitForSelector('.theme-noticeerror-font'), // timeout
            mainpage.waitForSelector('.empty-inbox'), 
            mainpage.waitForSelector('.new-message'), 
            mainpage.waitForNavigation(),
        ]).catch(function (error) {
            throw new Error(error);
        });
        console.log('...done');

        if (await mainpage.$('.theme-noticeerror-font'))
        {
            console.log(account + '-- '+ line_num +' --> TIMEOUT')
            ACCOUNTSTACK.push(account);
            await mainpage.screenshot({path:  './timeout'+line_num+'.jpg'  });
            return 'fatalerror';

        }
        else if (await mainpage.$('.empty-inbox'))
        {
            console.log(account + '-- '+ line_num +' --> empty');
            wite_to_file('empty.txt' , account );

        }
        else if (await mainpage.$('.new-message'))
        {
            console.log(account + '-- '+ line_num +' --> new message')
            wite_to_file('newmsg.txt' , account );

        }



    }
    catch(e)
    {
        console.log(`--------ERRRO----${account}-${line_num}---------------------`);
        await mainpage.screenshot({path:  './images/error'+line_num+'.jpg'  });
        ACCOUNTSTACK.push(account);
        const html =  await mainpage.content();
        fs.writeFileSync('./images/error'+line_num+'.html', html);

    }
}


function wite_to_file( file , acc){

    fs.appendFile('./' + file ,   `${acc}\n` , function (err) {})

}

我有时会在我的 console.logs 之间收到此错误:

(node:17535) MaxListenersExceededWarning: Possible EventEmitter memory leak dete       cted. 11 lifecycleevent listeners added to [FrameManager]. Use emitter.setMaxLis    teners() to increase limit
(node:17535) MaxListenersExceededWarning: Possible EventEmitter memory leak dete       cted. 11 framenavigatedwithindocument listeners added to [FrameManager]. Use emi    tter.setMaxListeners() to increase limit

我不确定是什么原因造成的,或者它是否严重?

使用--trace-warnings运行代码后

(node:992) MaxListenersExceededWarning: Possible EventEmitter memory leak detected. 11 framedetached listeners added to [FrameManager]. Use emitter.setMaxListeners() to increase limit
    at _addListener (events.js:261:17)
    at FrameManager.addListener (events.js:277:10)
    at Function.addEventListener (/home/robot/node_modules/puppeteer/lib/helper.js:188:13)
    at new NavigatorWatcher (/home/robot/node_modules/puppeteer/lib/NavigatorWatcher.js:50:14)
    at Page.waitForNavigation (/home/robot/node_modules/puppeteer/lib/Page.js:618:21)
    at open_tab (/home/robot/psn.js:212:22)
    at processTicksAndRejections (internal/process/task_queues.js:85:5)
    at async init (/home/robot/psn.js:115:21)
(node:992) MaxListenersExceededWarning: Possible EventEmitter memory leak detected. 11 response listeners added to [NetworkManager]. Use emitter.setMaxListeners() to increase limit
    at _addListener (events.js:261:17)
    at NetworkManager.addListener (events.js:277:10)
    at Function.addEventListener (/home/robot/node_modules/puppeteer/lib/helper.js:188:13)
    at Page.waitForNavigation (/home/robot/node_modules/puppeteer/lib/Page.js:621:29)
    at open_tab (/home/robot/psn.js:212:22)
    at processTicksAndRejections (internal/process/task_queues.js:85:5)
    at async init (/home/robot/psn.js:115:21)

【问题讨论】:

  • 这发生在我运行带有异步函数的循环时。就像从数据库中拉出用户一样。我倾向于确保我现在在 for(.. of ..) 循环中等待所有内容,这样就没有并行事件调用(并且我的脚本日志更易于阅读)。
  • @max,您在开始运行程序后一段时间或立即收到错误消息?
  • @max,你能像node --trace-warnings app.js一样运行你的应用程序,看看跟踪告诉你什么吗?
  • @max,这有什么更新吗?
  • @TarunLalwani 抱歉耽搁了……我已在我的问题底部添加了请求的数据

标签: javascript node.js puppeteer


【解决方案1】:

这通常发生在调用了太多异步函数而没有完成时。并且鉴于所有异步函数都有一个最小超时(0ms),它们总是被推到堆栈的末尾,并且它们都不会在循环调用它们中的许多之前完成。

无论如何,如果您要让大量异步事件并行发生,

通过将 MaxListeners 设置为 0 来禁用它

process.setMaxListeners(0);

或者,您可以使用 for 循环(而不是 forEach),将其包装在带有等待的异步函数中,以使您的代码完全同步。如果运行时间不是对象,这可以使日志记录更好。

// example of de-parallelizing
(async () => {
  const userIds = await myDb.getAllUserIds();
  const usersWithoutFirstnames = [];

  for (userId of userIds) {
    console.log("Getting first name of user:", userId);
    const firstName = await myDb.getFirstName(userId);

    if (firstName) {
      console.log("SUCCESS: ", firstName);
    } else {
      console.log("First name not found");
      usersWithoutFirstnames.push(userId);
    }
  }
})(); // closures are back

【讨论】:

  • thanx,我在 while 循环中调用异步函数,可能是这样....但是我在调​​用它们时使用等待...所以它们不能并行...对吗? while( inprogress ) { var check = await open_tab(account , line_number ,mainpage);}
  • 是的,这真的应该在继续之前完成每个。除非有一个不等待的包装循环(任何 forEach),或者 open_tab 出于某种原因在等待后未完成。也许它有一些不值得阻塞线程的异步记账功能?
  • 我现在将第五个函数添加到代码中,我认为它与问题无关,但现在我认为可能是它
【解决方案2】:

我认为问题与这些部分有关:

setInterval(function(){
  if(!inprogress)
    {
      init();
    }
}, 2000);
async function init(){

  ...

  browser._process.once('close', () => {
    console.log(' ------------------------  closed !');
    inprogress = false;
  });

  browser.on('disconnected', () => {
    console.log(' ------------------------  disconnecte !');
    inprogress = false;
  });

  ...

}

您每 2 秒触发两个事件侦听器(如果 inprogress 等于 false),但您仅在进程遇到 fatalerror 时关闭这些侦听器。

if(check === 'fatalerror') {
  await browser.close();
}

所以在您的代码逻辑中,我认为大多数 closedisconnected 侦听器仍在侦听/等待事件发生,就像您收到的警告一样,您可能同时拥有超过 11 个侦听器时间。您应该始终关闭 browser 实例(除非您想重用浏览器实例,这似乎不适合您的情况,因为每个浏览器实例都有不同的 ipport),我认为这两个事件侦听器都可以使用并适当释放内存。

所以也许你想总是像这样关闭浏览器:

async function init(){
  ...
  while(inprogress) {
    ...  
  }
  await browser.close();
}

【讨论】:

  • @max,你试过这个并有任何反馈吗?任何进一步的信息都可能有助于解决这个问题
  • 嗨,感谢您的 asnwer 并为延迟感到抱歉 .... interval 仅在 inprogress == false 时调用 init 函数,并且因为在 init 函数的第一行我设置了 @ 987654336@ ...不可能每 2 秒触发一次侦听器吧?
  • @max,是的,但即使你没有触发新的init 任务,而inprogress = true,进程也不会关闭每个init 任务的浏览器实例,除非它遇到@987654340 @,所以老听众还在
猜你喜欢
  • 1970-01-01
  • 2020-02-27
  • 2021-07-17
  • 1970-01-01
  • 2018-08-13
  • 1970-01-01
  • 2022-10-14
相关资源
最近更新 更多