2011-11-05 70 views
6

我想構建一個平臺來自動部署我在NodeJS中構建的應用程序。 每個應用程序都可以從處理這些操作的控制面板啓動和停止。 啓動應用程序時,控制面板執行以下操作:如何正確處理子進程的句柄

  • 從以root身份運行的端口綁定程序請求句柄。此端口綁定器是控制面板的分叉子。
  • 現在控制面板會偵聽此端口上的請求。一旦收到請求,就會將客戶端套接字的句柄發送到正在偵聽新消息的'消息'事件的應用程序。
  • 一旦應用程序發出信號,控制面板將釋放對請求和套接字的所有控制權。
  • 該應用程序執行它所需要的操作,並釋放並關閉請求和套接字。

此設置運行良好,除非嘗試關閉不再需要的服務器。 即使服務器發出'close'事件,它仍然接受連接,最終會超時,因爲沒有再綁定requestListener。

我假設有一個句柄沒有正確釋放,從而保持服務器清醒。

端口粘結劑:

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

控制面板:

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

應用:

正如你可以看到我試圖爲null,拆下所有_handle屬性我有,但服務器句柄不停止監聽端口。 當我讓控制面板有一個未捕獲的異常它崩潰,端口綁定程序也是如此。但是應用程序節點進程沒有退出,並且端口保持綁定狀態,所以我想知道我沒有正確地發佈某些內容,但我無法找到它。

整個安裝程序確實有效,當我提出要求時,根本沒有關閉服務器。

編輯:我也許應該注意,我如何使用Node.js v0.5.10

+0

你有沒有試着用v0.6.0?我相信他們已經增加了更多的功能,爲孩子今天處理 –

+0

安裝v0.6.0,並且問題仍然存在。這些文檔沒有描述任何新內容,今天晚些時候我會看到源代碼,也許它根本沒有記錄。 –

回答

8

事實證明它是Node.js的文件,導致這一問題的誤解。 我使用的瀏覽器發送了一個保持活動狀態的標題,保持連接打開一段時間,以通過相同的連接推送新的請求。

當我做了server.close()我期望這些連接被關閉,因爲該文件說:接受新連接

停止服務器。

事實證明,保持活動連接並未關閉,所以只要連接打開,瀏覽器仍然可以處理請求,並讓我知道服務器仍在接受連接。

解決方法是在關閉它之後,將requestListener重新附加到服務器。然後,當您收到連接時,關閉套接字流而不發送任何內容。現在不會有新的請求被處理,並且只要連接超時就會關閉,服務器將最終完全關閉。從而釋放了其他用途的端口。

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

將此修復程序就位我也可以清理我的代碼並刪除代碼以釋放句柄。 node.js垃圾收集器現在正確地拾取了句柄對象,並且當連接死亡時,句柄也一樣。

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