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

新手上路,请多包涵

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

问题

(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 代码是我在 Redis 中使用 socket.io 的问题。这是错误

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 => {
    /* To find the User Login  */
    let passport = socket.handshake.session.passport;

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

原文由 Nane 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 9.8k
2 个回答

事件发射 器的默认限制为 10。您可以使用 emitter.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) => {
    // 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));
      });

      // this is where you are subscribing for each and every socket connected to your server
      sub.on('message', (ch, msg) => {
        // this is the Exact line where I am getting this error

        // whereas you are emitting messages on socket manager, not on the 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));
      });
    }
  });
};

原文由 Rohit Harkhani 发布,翻译遵循 CC BY-SA 4.0 许可协议

The built-in events module in node.js (a version of which is bundled into your frontend app if you compile with webpack or browserify ) 对您的代码做出一些假设。有时,某处,有人决定,如果您有 X 注册的侦听器数量,那么您 肯定 有内存泄漏。有时它是正确的,并正确地提醒您去查找泄漏。

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


问题 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 个元素。这意味着(按设计!)您正在呈现 MyComponent 的 250 个实例,每个实例都在注册另一个唯一的事件侦听器。

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

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

如果你决定你已经完成了你的功课,并仔细检查了所有的事情,并且(通过设计!)注册了大量的事件监听器,那么最简单的解决方案是切换到使用 eventemitter3 ,这是 node 的 events 模块的替代品,除了速度更快,与浏览器兼容,并且 没有为您设置最大听众限制。

用法就像内置的 events 模块:

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

原文由 flintinatux 发布,翻译遵循 CC BY-SA 4.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题