web-dev-qa-db-fra.com

MaxListenersExceededWarning: possible fuite de mémoire EventEmitter détectée. 11 messages ajoutés. Utilisez emitter.setMaxListeners () pour augmenter la limite

Je sais que cela peut être considéré comme une solution dupliquée, mais le dépassement de capacité de la solution ne fonctionne pas pour moi.

Problème:

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

Ma base de code est énorme et je suis parfois confronté à cette erreur, mais je ne sais pas pourquoi.

Ce que j'ai essayé:

J'ai essayé d'augmenter la limite d'auditeurs, mais malheureusement, cela ne fonctionne pas.

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

METTRE À JOUR:

Après un peu de navigation, je lance cette commande pour suivre le parcours

node --trace-warnings index.babel.js

Il s’avère que mon code socket.io est le problème que j’utilise socket.io avec redis

c'est l'erreur

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)

c'est le code (mais ce code est pour des tâches plus spécifiques qu'il n'exécutera pas tout le temps)

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

La limite par défaut pour Event Emitter est de 10. Vous pouvez l'augmenter avec emitter.setMaxListeners. Ma suggestion est de ne pas le changer, à moins que et jusqu'à ce que cela soit explicitement requis, les auditeurs sont augmentés parce que vous n'êtes pas désabonné. Maintenant à votre 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)})
            })


        }
    });
};

Maintenant, si nous analysons le code ci-dessus, si vous ouvrez une connexion à 20 sockets sur votre serveur, celui-ci s'abonnera 20 fois. Maintenant, si votre exigence est d'écouter le message publié sur redis au niveau du serveur, puis d'émettre sur io, votre code devrait être comme ci-dessous

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

Le module intégré events de node.js (dont une version est fourni dans votre application frontale si vous compilez avec webpack ou browserify ) fait des suppositions sur votre code. Quelquefois, quelque part, quelqu'un a décidé que si vous aviez X nombre d'auditeurs enregistrés, alors sûrement vous avez une fuite de mémoire. Et parfois, il est correct, et vous rappelle correctement d'aller chercher les fuites.

J'ai reçu cet avertissement à plusieurs reprises, mais en général uniquement pour deux raisons spécifiques, qui offrent toutes deux des solutions simples:


Problème 1: Fonctions de l'écouteur d'événements liés incompatibles

Votre composant peut avoir l'aspect suivant: vous utilisez une méthode de composant comme écouteur d'événement et vous le liez en l'enregistrant.

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

Le problème ici est que function.bind crée chaque fois une nouvelle fonction, de sorte que la fonction que vous essayez de supprimer ne soit pas la même que celle que vous avez ajoutée. Par conséquent, les fonctions ajoutées continuent de s’ajouter (mauvais jeu de mots) et vous avez en fait une véritable fuite de mémoire.

Solution 1: Liez vos méthodes tôt

Liez votre méthode tôt, généralement dans la constructor(). Ensuite, vous pouvez faire référence à la version liée à chaque fois, en vous assurant que la fonction supprimée est la même que la fonction ajoutée.

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

Problème 2: Beaucoup d'auditeurs d'événements

Parfois, vous avez vraiment fait vos devoirs et vérifié deux fois que vous avez lié vos auditeurs tôt au besoin, puis que vous les avez tous retirés aux endroits appropriés. Ensuite, vous regardez de plus près et constatez que vous faites quelque chose comme ceci:

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

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

Supposons que this.props.largeArray ait 50, 100 ou peut-être 250 éléments. Cela signifie que (à dessein!), Vous restituez 250 instances de MyComponent, chacune d’elles enregistrant un autre écouteur d’événement unique.

Ne crains pas! Ce code est totalement valide et ne présente pas de fuite de mémoire. Mais cela dépasse la limite du nombre maximum d'auditeurs, à savoir que quelqu'un, parfois, quelque part, ait décidé arbitrairement de contribuer à vous protéger.

Solution 2: utilisez eventemitter3

Si vous décidez que vous avez bien fait vos devoirs, vérifié toutes les choses et que vous enregistrez (de par leur conception!) De nombreux écouteurs d'événements, la solution la plus simple consiste à passer à l'utilisation de eventemitter3 est un remplacement instantané du module events du nœud, sauf que le module est plus rapide et compatible avec le navigateur, et que ne définit pas de limite maximale d'écoute pour vous.

L'utilisation est similaire au module events intégré:

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

Il s'agit de la méthode recommandée pour ajouter et supprimer des écouteurs d'événement dans les méthodes React Components - à l'aide de 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>
    );
  }
}

Il est important de se rappeler que window se situe dans le contexte d'exécution globale. Par conséquent, chaque fois que vous ajoutez un écouteur d'événement, vous demandez à l'étendue globale de 

  1. instancier un autre auditeur. 
  2. suivre cet écouteur en utilisant la mémoire globale par référence - dans ce cas, resize
  3. continuez à suivre l'auditeur jusqu'à ce qu'on vous dise de ne pas le faire.

Si vous ne dites jamais à l'étendue globale de supprimer ces écouteurs, la mémoire globale - allouée par les paramètres de votre navigateur - s'évaporera lentement et plantera votre navigateur et votre application, ou le navigateur du client s'il est déjà en production. Il faut être très prudent et très conscient quand on manipule la mémoire globale.

Si vous voulez comprendre (si vous ne l'avez pas déjà) POURQUOI les méthodes de cycle de vie du travail de React Component, je vous recommande fortement de regarder ici le cycle de vie de réconciliation de React. On ne peut pas s’appeler avec précision un «développeur de la réaction»} et ne pas être intimement familier avec réconciliation.

Note Ce composant utilise babel pour transpiler des parties du code: import, et a attribué la méthode personnalisée handleResize à l'aide de fonctions de flèche uniquement. Si vous avez besoin d’aide pour la configuration de l’environnement, vous pouvez vous référer à cet article de blog i a écrit que cela devrait le rendre compréhensible. 

Bonne chance.

0
Tobiah Rex