2011-11-05 11 views
6

Sto provando a creare una piattaforma per l'auto-distribuzione delle applicazioni che compongo in NodeJS. Ogni applicazione può essere avviata e arrestata da un pannello di controllo che gestisce queste operazioni. Il pannello di controllo fa le seguenti cose quando viene avviata un'applicazione:Come smaltire correttamente gli handle dai processi secondari

  • richiede una maniglia da una porta-legante che esegue come root. Questo port-binder è un figlio biforcuto del pannello di controllo.
  • Ora il pannello di controllo ascolta le richieste su questa porta. Una volta ricevuta una richiesta, invia l'handle del socket client all'applicazione che sta ascoltando l'evento 'message' per le nuove richieste.
  • Una volta segnalata l'applicazione, il pannello di controllo rilascia tutto il controllo sulla richiesta e sulla presa.
  • L'applicazione fa ciò che deve fare e rilascia e chiude anche la richiesta e il socket.

Questa configurazione funziona bene, tranne quando si tenta di abbattere un server che non è più necessario. Anche se il server emette l'evento "close", accetta comunque la connessione, che alla fine si interrompe perché non vi è più alcun binding di richiesta.

Suppongo che ci sia un handle che non è stato rilasciato correttamente e quindi mantiene il server sveglio.

Port legante: pannello

var options = { 
    http: require('http'), 
    https: require('https') 
}; 
process.on('message',function(data, handle) { 

    var port = data.port, 
      type = data.type, 
      server; 

    server = options[type].createServer(); 
    server.once('error', function(err) { 
     process.send({success: false, port: port, error: err}); 
    }); 
    server.listen(port,function(){ 
     var handle = server._handle; 

     process.send({success: true, port: port}, handle); 

     server.close(); 
     handle.close(); 
     server._handle = null; 
     handle = null; 
     server.removeAllListeners(); 
    }); 
}); 

controllo:

var cp = require('child_process'), 
    app, 
    types = { 
     http: require('http'), 
     https: require('https') 
    }, 
    binder = cp.fork('./portbinder'); 

binder.on('message', function(data, handle) { 
    var server = types['http'].createServer(handler); 

    server.once('close', function() { 
     server._handle = null; 
     handle = null; 
    }); 
    server.listen(handle); 
}); 

function handler(req, resp) { 
    app = app || cp.fork('./application'), 
    socket = resp.socket, 

    _handle = socket._handle, 
    _req = {}, 
    _resp = {}, 
    _socket = {}; 

    // Copy transferable fields. 
    for(i in req) { 
     if(typeof req[i] != 'function' && i != 'socket' && i != 'connection' && i != 'client') { 
      _req[i] = req[i]; 
     } 
    } 

    for(i in resp) { 
     if(typeof resp[i] != 'function' && i != 'socket' && i != 'connection' && i != 'client') { 
      _resp[i] = resp[i]; 
     } 
    } 

    for(i in socket) { 
     if(typeof socket[i] != 'function' && 
      i != '_handle' && i != '_httpMessage' && 
      i != '_idlePrev' && i != '_idleNext' && i != 'server') { 
       _socket[i] = socket[i]; 
     } 
    } 

    // Send request to application. 
    app.send({ 
     type: 'request', 
     data: { 
      req: _req, 
      resp: _resp, 
      socket: _socket 
     } 
    }, _handle); 

    // Release the handle here without sending anything to the browser. 
    socket._write = function() {}; 
    resp.end(); 
    socket.destroy(); 
    socket._handle = null; 
    _handle = null; 
}; 

Applicazioni:

Come si può vedere che sto cercando su null e staccare tutte le proprietà _handle I avere, ma l'handle del server non interrompe l'ascolto sulla porta. Quando faccio sì che il pannello di controllo abbia un'eccezione non rilevata, si blocca e anche il raccoglitore di porte fa altrettanto. Ma il processo del nodo dell'applicazione non si chiude e la porta rimane vincolata, quindi sto geussing che non sto rilasciando qualcosa correttamente, ma non riesco a trovarlo.

L'intera installazione funziona, non è semplicemente chiudere un server quando lo chiedo.

EDIT: ho probabilmente dovrebbe notare che sto usando node.js v0.5.10

+0

Hai provato con v0.6.0? Credo che hanno aggiunto ulteriori funzionalità per il bambino processi –

+0

v0.6.0 installato oggi, e il problema persiste. I documenti non descrivono nulla di nuovo, guarderò la fonte più tardi oggi, forse semplicemente non è documentato. –

risposta

8

Come si è visto si tratta di un'errata interpretazione della documentazione node.js che ha portato a questo problema. Il browser che stavo usando ha inviato un'intestazione keep-alive mantenendo le connessioni aperte per qualche tempo per inviare nuove richieste sulle stesse connessioni.

Quando ho fatto un server.close() mi aspettavo queste connessioni da chiudere perché i documenti dicono:

Interrompe il server di accettare nuove connessioni.

Come risulta, le connessioni keep-alive non sono chiuse, quindi il browser era ancora in grado di fare richieste fino a quando le connessioni erano aperte, dandomi l'idea che il server stava ancora accettando le connessioni.

La soluzione è ricollegare una richiestaListener al server dopo la chiusura. Quindi quando si riceve una connessione, chiudere lo stream del socket senza inviare nulla. Ora non verranno gestite nuove richieste e non appena la connessione scade, si chiuderà e il server si spegnerà definitivamente. Liberando così il porto per altri usi.

server.close(); 
// Drop any new request immediately, while server ditches old connections. 
server.on('request', function(req, resp) { req.socket.end(); }); 
server.once('close', function(){ 
    // Remove the listeners after the server has shutdown for real. 
    server.removeAllListeners(); 
}); 

Mettendo questa correzione in atto potrei anche pulire il mio codice e rimuovere il codice per rilasciare la maniglia. node.js garbage collector ora preleva correttamente gli oggetti handle e quando la connessione muore, lo stesso vale per l'handle.

// All of this is no longer needed. 
socket._write = function() {}; 
socket.end(); 
socket.destroy(); 
socket._handle = null; 
handle = null; 
Problemi correlati