web-dev-qa-db-ger.com

MaxListenersExceededWarning: Möglicher EventEmitter-Speicherverlust erkannt. 11 Nachrichtenspeicher hinzugefügt. Verwenden Sie emitter.setMaxListeners (), um das Limit zu erhöhen

Ich weiß, dass dies als doppelte Lösung gekennzeichnet sein kann, aber die Lösung für den Stapelüberlauf funktioniert für mich nicht.

Problem:

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

Meine Codebasis ist riesig und ich stehe vor diesem Fehler. Manchmal weiß ich nicht, warum es passiert

Was ich probiert habe:

Es wurde versucht, das Hörerlimit zu erhöhen, aber es funktioniert leider nicht.

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

UPDATE:

Nach einigem Durchsuchen führe ich diesen Befehl aus, um das Wanrning zu verfolgen

node --trace-warnings index.babel.js

Es stellt sich heraus, dass mein socket.io-Code das Problem ist, das ich socket.io mit redis verwende

das ist der fehler

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)

dies ist der Code (aber dieser Code ist für spezifischere Aufgaben, die nicht immer ausgeführt werden)

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


        }
    });
};
5
Nane

Das Standardlimit für Event Emitter ist 10. Sie können es mit emitter.setMaxListeners erhöhen. Mein Vorschlag ist, es nicht zu ändern, es sei denn, es ist ausdrücklich erforderlich, die Anzahl der Zuhörer zu erhöhen, da Sie sich nicht abgemeldet haben. Nun zu deinem Code.

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


        }
    });
};

Wenn wir nun den obigen Code analysieren, wird er 20 Mal abonniert, wenn Sie eine 20-Socket-Verbindung zu Ihrem Server öffnen. Hier geht es schief. Wenn jetzt Ihre Anforderung darin besteht, die auf redis auf Serverebene veröffentlichte Nachricht abzuhören und dann auf io auszugeben, sollte Ihr Code wie folgt aussehen

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


        }
    });
};

4
Rohit Harkhani

Das eingebaute events module in node.js (dessen Version in Ihrer Frontend-App enthalten ist, wenn Sie mit webpack oder browserify kompilieren) macht einige Annahmen über Ihren Code. Irgendwann entschied irgendjemand, dass, wenn Sie X Anzahl der registrierten Hörer registriert hätten, sicherlich Sie einen Speicherverlust haben. Und manchmal ist es richtig und erinnert Sie richtig daran, die Lecks zu finden.

Ich habe diese Warnung oft erhalten, normalerweise jedoch nur aus zwei Gründen, die beide einfache Lösungen haben:


Problem 1: Nicht übereinstimmende Listener-Funktionen für gebundene Ereignisse

Ihre Komponente kann so aussehen, wenn Sie eine Komponentenmethode als Ereignis-Listener verwenden und sie binden wie Sie sie registrieren.

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

Das Problem hierbei ist, dass function.bind jedes Mal eine neue Funktion erstellt, sodass die Funktion, die Sie entfernen möchten, nicht mit der von Ihnen hinzugefügten Funktion identisch ist. Folglich addieren sich die hinzugefügten Funktionen (schlechtes Wortspiel) und Sie haben tatsächlich einen echten Speicherverlust.

Lösung 1: Binden Sie Ihre Methoden frühzeitig

Binden Sie Ihre Methode frühzeitig in der constructor(). Dann können Sie jedes Mal auf die gebundene Version verweisen, um sicherzustellen, dass die entfernte Funktion mit der hinzugefügten Funktion übereinstimmt.

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

Problem 2: Viele, viele Event-Hörer

Manchmal haben Sie Ihre Hausaufgaben wirklich gemacht und noch einmal überprüft, ob Sie Ihre Zuhörer bei Bedarf frühzeitig gebunden haben, und sie dann an den entsprechenden Stellen entfernt. Dann schaust du genauer hin und stellst fest, dass du so etwas machst:

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

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

Angenommen, this.props.largeArray hatte 50, 100 oder vielleicht 250 Elemente. Das bedeutet, dass Sie (von Entwurf!) 250 Instanzen von MyComponent rendern, von denen jede einen anderen eindeutigen Ereignis-Listener registriert.

Keine Angst! Dies ist vollständig gültiger Code und hat keinen Speicherverlust. Aber es durchbricht die maximale Anzahl von Hörern, dass irgendjemand irgendwann irgendwo willkürlich beschlossen hat, Sie zu schützen.

Lösung 2: Wechseln Sie zur Verwendung von eventemitter3

Wenn Sie sich entschieden haben, Ihre Hausaufgaben gemacht zu haben und alle Dinge noch einmal überprüft zu haben und (von Entwurf!) Viele Event-Listener registrieren, ist die einfachste Lösung die Verwendung von eventemitter3 Drop-In-Ersetzung für das events-Modul des Knotens, außer schneller und browserkompatibel, und setzt keine maximale Anzahl von Hörern für Sie fest.

Die Verwendung ist wie beim eingebauten Modul events:

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

Dies ist die empfohlene Methode zum Hinzufügen und Entfernen von Ereignis-Listenern innerhalb von React -Komponenten - mit LifeCycle -Methoden. 

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

Denken Sie daran, dass sich window im globalen Ausführungskontext befindet. Daher müssen Sie jedes Mal, wenn Sie einen Ereignis-Listener hinzufügen, den globalen Gültigkeitsbereich angeben 

  1. einen anderen Listener instanziieren. 
  2. verfolgen Sie diesen Listener anhand des globalen Speichers anhand der Referenz - in diesem Fall resize
  3. verfolgen Sie den Zuhörer weiter, bis Sie dazu aufgefordert werden.

Wenn Sie niemals den globalen Bereich zum Entfernen dieser Listener anweisen, wird der globale Speicher - wie von Ihren Browsereinstellungen zugewiesen - langsam verflüchtigt und stürzt Ihren Browser und Ihre Anwendung oder den Browser des Clients ab, wenn dieser bereits in Produktion ist. Man muss SEHR vorsichtig und SEHR aufmerksam sein, wenn sie den globalen Speicher manipulieren.

Wenn Sie verstehen möchten (warum Sie dies nicht bereits tun), warum die Lebenszyklusmethoden in React Component arbeiten, würde ich Ihnen dringend empfehlen, hier unter Reacts Reconciliation - Lebenszyklus zu suchen. Man kann sich nicht genau als "React Developer" bezeichnen und sich mit Reconciliation nicht auskennen.

Note Diese Komponente verwendet babel, um Teile des Codes: import und die benutzerdefinierte Methode handleResize nur mit Pfeilfunktionen zu transpilieren. Wenn Sie Hilfe beim Einrichten der Umgebung benötigen, können Sie sich auf diesen Blog-Beitrag beziehen. _ Ich habe geschrieben, dass es verständlich werden sollte. 

Viel Glück.

0
Tobiah Rex