MaxListenersExceededWarning:检测到可能的EventEmitter内存泄漏。添加了11条消息lis teners。使用emitter.setMaxListeners()来增加限制

时间:2018-06-05 21:16:56

标签: javascript mysql node.js reactjs

我知道这可能会标记为重复的解决方案,但堆栈溢出的解决方案对我来说不起作用。

问题:

(node:5716) MaxListenersExceededWarning: Possible EventEmitter memory leak detected. 11 message lis
teners added. Use emitter.setMaxListeners() to increase limit.

我的代码库很大,我遇到这个错误有时候我不知道为什么会发生这种情况

我尝试了什么:

尝试增加听众限制,但遗憾的是它无效。

const EventEmitter = require('events');
const emitter = new EventEmitter()
emitter.setMaxListeners(50)

更新

经过一些浏览后,我运行此命令来追踪漫游

node --trace-warnings index.babel.js

原来是我的socket.io代码是我使用socket.io和redis的问题

这是错误

node:14212) MaxListenersExceededWarning: Possible EventEmitter memory leak detected. 11 message li
steners added. Use emitter.setMaxListeners() to increase limit
    at _addListener (events.js:281:19)
    at RedisClient.addListener (events.js:298:10)
    at Namespace.<anonymous> (D:/newProject/services/socket.js:21:17)
    at emitOne (events.js:115:13)
    at Namespace.emit (events.js:210:7)
    at Namespace.emit (D:\newProject\node_modules\socket.io\lib\namespace.js:213:10)
    at D:\newProject\node_modules\socket.io\lib\namespace.js:181:14
    at _combinedTickCallback (internal/process/next_tick.js:131:7)
    at process._tickCallback (internal/process/next_tick.js:180:9)

这是代码(但是这段代码用于更具体的任务,它不会一直执行)

const redis = require('redis');
const config = require('../config')
const sub = redis.createClient(config.REDIS.port, config.REDIS.host);
const pub = redis.createClient(config.REDIS.port, config.REDIS.host);

sub.subscribe('spread');


module.exports = io => {
    io.on('connection',(socket) => {

        let passport  = socket.handshake.session.passport;  /* To find the User Login  */
        if(typeof passport !== "undefined") {


            socket.on('typing:send',(data) => {

                pub.publish('spread',JSON.stringify(data))
            });
            sub.on('message',(ch,msg) => { // this is the Exact line where I am getting this error


                io.emit(`${JSON.parse(msg).commonID}:receive`,{...JSON.parse(msg)})
            })


        }
    });
};

4 个答案:

答案 0 :(得分:4)

Event Emitter的默认限制为10。您可以使用generator.setMaxListeners增大它。我的建议是不要更改它,除非并且直到明确要求为止,因为您没有取消订阅,因此增加了侦听器。现在进入您的代码。

const redis = require('redis');
const config = require('../config')
const sub = redis.createClient(config.REDIS.port, config.REDIS.host);
const pub = redis.createClient(config.REDIS.port, config.REDIS.host);

sub.subscribe('spread');


module.exports = io => {
    io.on('connection',(socket) => {
    //COMMENT : This callback will be executed for all the socket connections. 
        let passport  = socket.handshake.session.passport;  /* To find the User Login  */
        if(typeof passport !== "undefined") {


            socket.on('typing:send',(data) => {

                pub.publish('spread',JSON.stringify(data))
            });
            // COMMENT : This is where you are subscribing for each and every socket conected to your server
            sub.on('message',(ch,msg) => { // this is the Exact line where I am getting this error


//COMMENT : Where as you are emiting message on socket manager not on socket. 
io.emit(`${JSON.parse(msg).commonID}:receive`,{...JSON.parse(msg)})
            })


        }
    });
};

现在,如果我们分析上述代码,则如果您打开与服务器的20个套接字连接,它将订阅20次,这是错误的。 现在,如果您的要求是在服务器级别上侦听在Redis上发布的消息,然后在io上发出消息,那么您的代码应如下所示

const redis = require('redis');
const config = require('../config')
const sub = redis.createClient(config.REDIS.port, config.REDIS.host);
const pub = redis.createClient(config.REDIS.port, config.REDIS.host);

sub.subscribe('spread');


module.exports = io => {
sub.on('message',(ch,msg) => { // this is the Exact line where I am getting this error


                io.emit(`${JSON.parse(msg).commonID}:receive`,{...JSON.parse(msg)});
        });
    io.on('connection',(socket) => {

        let passport  = socket.handshake.session.passport;  /* To find the User Login  */
        if(typeof passport !== "undefined") {


            socket.on('typing:send',(data) => {

                pub.publish('spread',JSON.stringify(data))
            });
            


        }
    });
};

答案 1 :(得分:3)

events中的内置node.js模块(如果您使用webpackbrowserify进行编译,则该模块捆绑在您的前端应用程序中)对您的代码。有时,某处某人认为如果您注册了X个侦听器,那么<​​em>确定肯定会发生内存泄漏。有时它是正确的,并会提醒您正确查找泄漏。

我已经多次收到此警告,但是通常仅出于两个特定原因,这两个原因都有简单的解决方案:


问题1:绑定事件侦听器功能不匹配

您的组件可能看起来像这样,您使用组件方法作为事件侦听器,并在注册时绑定

import events from '../lib/events' // some singleton event emitter

class MyComponent extends React.Component {
  componentDidMount() {
    events.addEventListener('some-event', this.myMethod.bind(this))
  }

  componentWillUnmount() {
    events.removeEventListener('some-event', this.myMethod.bind(this))
  }

  myMethod() {
    // does something
  }

  render() {
    // gotta have this too
  }
}

这里的问题是function.bind每次都会创建一个新功能,因此您要删除的功能与您添加的功能不同。因此,添加的功能会不断累加(坏双关),实际上您确实存在内存泄漏。

解决方案1:尽早绑定您的方法

尽早绑定方法,通常在constructor()中。然后,您可以每次都引用绑定版本,以确保删除的功能与添加的功能相同。

import events from '../lib/events' // some singleton event emitter

class MyComponent extends React.Component {
  constructor() {
    // bind your method early so the function removed
    // is the same as the function added
    this.myMethod = this.myMethod.bind(this)
  }

  componentDidMount() {
    events.addEventListener('some-event', this.myMethod)
  }

  componentWillUnmount() {
    events.removeEventListener('some-event', this.myMethod)
  }

  myMethod() {
    // does something
  }

  render() {
    // gotta have this too
  }
}

问题2:大量的事件监听器

有时候,您确实完成了家庭作业,并仔细检查了是否已根据需要提前绑定了听众,然后将它们全部移到适当的位置。然后仔细观察,发现您正在执行以下操作:

import MyComponent from './MyComponent' // same component above

class Parent extends React.Component {
  render() {
    return (
      <div>
        { this.props.largeArray.map(MyComponent) }
      </div>
    )
  }
}

假设this.props.largeArray有50、100或250个元素。这意味着(通过设计!)您正在渲染250个MyComponent实例,每个实例都在注册另一个唯一的事件侦听器。

不要害怕!这是完全有效的代码,并且没有内存泄漏。但这确实突破了有人在某个时间任意决定帮助保护您的最大听众限制。

解决方案2:切换为使用eventemitter3

如果您确定已经完成家庭作业,并仔细检查了所有内容,并且(根据设计)正在注册许多事件侦听器,那么最简单的解决方案是切换为使用eventemitter3,它是节点events模块的直接替代品,但速度更快且与浏览器兼容,并且不会为您设置最大收听者限制。

用法就像内置的events模块一样:

const EventEmitter = require('eventemitter3')
const emitter = new EventEmitter()

答案 2 :(得分:2)

MaxListenersExceededWarning:可能的EventEmitter内存泄漏 检测到。添加了 11 条消息teners。采用 generator.setMaxListeners()增加限制

默认情况下,任何单个事件最多可以注册 10 个侦听器,而我们得到的 11 大野

// Change to 80 or 150 whatever and see what happens
require('events').EventEmitter.prototype._maxListeners = 70;
require('events').defaultMaxListeners = 70;

  process.on('warning', function (err) {
    if ( 'MaxListenersExceededWarning' == err.name ) {
      console.log('o kurwa');
      // write to log function
      process.exit(1); // its up to you what then in my case script was hang

    }
  });

答案 3 :(得分:0)

这是在 React 组件中添加和删除事件侦听器的推荐方法 - 使用 LifeCycle 方法。

import { Component } from 'react';

class Example extends Component {
  constructor(props) {
   super(props);

   this.state = {
    windowWidth: window.innderWidth,
   };
  }

  componentDidMount() {
    window.addEventListener('resize', this.handleResize);
  }

  componentWillUnmount() {
    window.removeEventListener('resize', this.handleResize);
  }

  handleResize = () => {
    this.setState({ windowWidth: window.innerWidth });
  }

  render() {
    return (
      <div>
        Current window width: {this.state.windowWidth}
      </div>
    );
  }
}

重要的是要记住window在全局执行上下文中。因此,每次添加事件侦听器时,都要求全局范围

  1. 实例化另一个侦听器。
  2. 通过引用跟踪该侦听器使用全局内存 - 在本例中为resize
  3. 继续跟踪听众直到被告知不要。
  4. 如果您从未告诉全局范围删除这些侦听器,那么全局内存(由浏览器设置分配)将慢慢消失并导致浏览器崩溃。应用程序,或客户端的浏览器(如果已经在生产中)。当他们操纵全局记忆时,必须非常小心并非常清楚。

    如果您想了解(如果您还没有)为什么React Component的生命周期方法有效,我强烈建议您在React的 Reconciliation 生命周期中查看here。人们无法准确地将自己称为“React Developer”而不是非常熟悉对帐

    注意此组件使用babel来转换部分代码:import,并使用箭头函数分配自定义方法handleResize。如果您在设置环境方面需要帮助,可以参考我写的this blog post,这应该让它变得可以理解。

    祝你好运。