2012-10-25 20 views
5

接続されている1つのソケットからデータを取り出し、接続された別のソケットに送信するnode.jsスクリプトを作成しています。テスト中に、サーバーが大量のデータを送信している間にクライアントを何度も切断して再接続すると、メモリリークが発生することに気付きました。以下はnode.jsコードです。node.jsスクリプトと可能性のあるメモリリーク

var net = require('net'); 
var logServer = net.createServer(); 
var clientList = []; 
var clientIPList = []; 
var serverList = []; 
var serverIPList = []; 
var port = 6451; 

logServer.on('connection', function(client) { 
    client.setEncoding('utf8'); 
    client.once('data', function(data) { 
     if (data[0].toString() == 'S') { 
      var server = client; 
      client = undefined; 
      serverList.push(server); 
      serverIPList.push(server.remoteAddress + ":" + server.remotePort); 
      console.log('Server connected: %s:%d', server.remoteAddress, server.remotePort); 

      server.on('data', function(data) { 
       for(var i=0;i<clientList.length;i+=1) { 
        try { 
         clientList[i].write(data); 
        } catch (err) { 
         console.log('Error writing to client "data event": ' + clientIPList[i]); 
         // close and null the socket on write error 
         try { 
          clientList[i] = null; 
          clientList[i].end(); 
         } catch (err) {} 
         clientList.splice(i, 1); 
         clientIPList.splice(i, 1); 
        } 
       }    
      }) 

      server.on('end', function() { 
       try { 
        var d; 
        if((d = serverList.indexOf(server)) != -1) { 
         console.log('Server disconnecting "end event": ' + serverIPList[d]); 
         try { 
          serverList[d] = null; 
          serverList[d].end(); 
         } catch (err) {} 
         serverList.splice(d, 1); 
         serverIPList.splice(d, 1); 
        } 
        else { 
         console.log('Server disconnecting "end event": unknown server');      
        } 
       } catch (err) { 
        console.log('Error cleaning up server socket list on "end event"'); 
       } 
      }) 

      server.on('timeout', function() { 
       try { 
        var d; 
        if((d = serverList.indexOf(server)) != -1) { 
         console.log('Server disconnecting "timeout event": ' + serverIPList[d]); 
         try { 
          serverList[d] = null; 
          serverList[d].end(); 
         } catch (err) {} 
         serverList.splice(d, 1); 
         serverIPList.splice(d, 1); 
        } 
        else { 
         console.log('Server disconnecting "timeout event": unknown server');      
        } 
       } catch (err) { 
        console.log('Error cleaning up server socket list on "timeout event"'); 
       } 
      }) 

      server.on('error', function(e) { 
       try { 
        var d; 
        if((d = serverList.indexOf(server)) != -1) { 
         console.log('Server disconnecting ' + e.code + ' "error event": ' + serverIPList[d]); 
         try { 
          serverList[d] = null; 
          serverList[d].end(); 
         } catch (err) {} 
         serverList.splice(d, 1); 
         serverIPList.splice(d, 1); 
        } 
        else { 
         console.log('Server disconnecting "error event": unknown server');     
        } 
       } catch (err) { 
        console.log('Error cleaning up server socket list on "error event"'); 
       } 
      }) 

      server.on('close', function() { 
       try { 
        var d; 
        if((d = serverList.indexOf(server)) != -1) { 
         console.log('Server disconnecting "close event": ' + serverIPList[d]); 
         try { 
          serverList[d] = null; 
          serverList[d].end(); 
         } catch (err) {} 
         serverList.splice(d, 1); 
         serverIPList.splice(d, 1); 
        } 
       } catch (err) { 
        console.log('Error cleaning up server socket list on "close event"'); 
       } 
      }) 
      server.on('drain', function() { 
      }) 
     } 

     else { 
      clientList.push(client); 
      clientIPList.push(client.remoteAddress + ":" + client.remotePort); 
      console.log('Client connected: %s:%d',client.remoteAddress, client.remotePort); 

      client.on('data', function(data) { 
       console.log('writing "%s" to %d servers', data.replace(/[\r\n]/g,''), serverList.length); 
       for(var i=0;i<serverList.length;i+=1) { 
        try { 
         serverList[i].write(data); 
        } catch (err) { 
         console.log('Error writing to server "data event": ' + serverIPList[i]); 
         try { 
          serverList[i] = null; 
          serverList[i].end(); 
         } catch (err) {} 
         serverList.splice(i, 1); 
         serverIPList.splice(i, 1); 
        } 
       } 
      }) 

      client.on('end', function() { 
       try { 
        var d; 
        if((d = clientList.indexOf(client)) != -1) { 
         console.log('Client disconnecting "end event": ' + clientIPList[d]); 
         // close and null the socket 
         try { 
          clientList[d] = null; 
          clientList[d].end(); 
         } catch (err) {} 
         clientList.splice(d, 1); 
         clientIPList.splice(d, 1); 
        } 
        else { 
         console.log('Client disconnecting "end event": unknown client'); 
        }    
       } catch (err) { 
        console.log('Error cleaning up socket client list on "end event"'); 
       } 
      }) 

      client.on('timeout', function() { 
       try { 
        client.end(); 
       } catch (err) { 
        var d; 
        if((d = clientList.indexOf(client)) != -1) { 
         console.log('Error closing client connection "timeout event": ' + clientIPList[d]); 
        } 
        else { 
         console.log('Error closing client connection "timeout event": unknown client');     
        } 
       }    
       try { 
        var d; 
        if((d = clientList.indexOf(client)) != -1) { 
         console.log('Client disconnecting "timeout event": ' + clientIPList[d]); 
         try { 
          clientList[d] = null; 
          clientList[d].end(); 
         } catch (err) {} 
         clientList.splice(d, 1); 
         clientIPList.splice(d, 1); 
        } 
        else { 
         console.log('Client disconnecting "timeout event": unknown client'); 
        }    
       } catch (err) { 
        console.log('Error cleaning up client socket list on "timeout event"'); 
       } 
      }) 

      client.on('error', function(e) { 
       try { 
        var d; 
        if((d = clientList.indexOf(client)) != -1) { 
         console.log('Client disconnecting ' + e.code + ' "error event": ' + clientIPList[d]); 
         try { 
          clientList[d] = null; 
          clientList[d].end(); 
         } catch (err) {} 
         clientList.splice(d, 1); 
         clientIPList.splice(d, 1); 
        } 
        else { 
         console.log('Client disconnecting ' + e.code + ' "error event": unknown client'); 
        }    
       } catch (err) { 
        console.log('Error cleaning up client socket list on "error event"'); 
       } 
      }) 

      client.on('close', function() { 
       try { 
        var d; 
        if((d = clientList.indexOf(client)) != -1) { 
         console.log('Client disconnecting "close event": ' + clientIPList[d]); 
         try { 
          clientList[d] = null; 
          clientList[d].end(); 
         } catch (err) {} 
         clientList.splice(d, 1); 
         clientIPList.splice(d, 1); 
        } 
       } catch (err) { 
        console.log('Error cleaning up client socket list on "close event"'); 
       } 
      }) 

      client.on('drain', function() { 
       // nothing 
      }) 
     } 
    }) 
}) 
logServer.listen(port); 

私の知る限り、私はすべての重要な「ネット」のイベントを渡していると私は切断を検出したら、私は、適切にソケットをクリーンアップしています。ここでは、私がテストに使用している2つのスクリプトです。最初のものはクライアントとして何度も接続したり切断したりするだけで、2番目のサーバーはサーバーとしてデータを送信します。私はそれらを同時に両方実行します。

condiscon.rb:自分自身をクライアントとして登録した後、接続して切断します。「接続後に改行を送信します」。私は「./condiscon.rb 1000'

#!/usr/bin/ruby 

require 'rubygems' 
require 'socket' 

def connectFlac 
    host = '10.211.55.10' 
    port = 6451 

    sock = TCPSocket.open(host, port) 
    sock.puts("") 
    sock 
end 

sock = connectFlac() 
data = [] 
user_agents = {} 
instances_lat = {} 

count = ARGV.shift.to_i 

while(count > 0) 
    sock = connectFlac() 
    sleep(0.05) 
    sock.close() 
    sleep(0.05) 
    count-= 1 
end 

dataflood.rbで実行します。サーバとして接続し、カウンターでABCDEの〜2600個のバイトのパケットを送信します。私は 'dataflood.rb 30000'を実行する

#!/usr/bin/ruby 

require 'socket' 

def connectFlac 
    host = '10.211.55.10' 
    port = 6451 

    sock = TCPSocket.open(host, port) 
    sock.setsockopt(Socket::IPPROTO_TCP,Socket::TCP_NODELAY,1) 
    sock.puts("S") 
    sock 
end 

def syntax() 
    print "./script number_of_packets\n" 
    exit(1) 
end 

data = "" 
(1..100).each { 
    data+= "abcdefghijklmnopqrstuvwxyz" 
} 

sock = connectFlac() 

numpackets = ARGV.shift.to_i || syntax() 
counter = 1 
byteswritten = 0 

while(numpackets > 0) 
    r,w,e = IO.select(nil, [sock], nil, nil) 
    w.each do |sock_write| 
     print numpackets, "\n" 
     sock.write(counter.to_s + "|" + data + "\n") 
     sock.flush() 
     byteswritten+= counter.to_s.length + 1 + data.length + 1 
     counter+= 1 
     numpackets-= 1 
    end 
end 
sock.close() 

print "Wrote #{byteswritten} bytes\n" 

私が見ている結果のいくつかを以下に示します。テストの前にlogserver.jsでメモリプロファイルを実行すると、約9メガバイトの常駐メモリが使用されます。私は、リークが占有しているように見えるメモリのセクションを示すpmapを含めています。ここで雨の時にlogserverに対する上記の二つのRubyスクリプトを実行した後

[[email protected] ~]# ps vwwwp 20658 
    PID TTY  STAT TIME MAJFL TRS DRS **RSS** %MEM COMMAND 
20658 pts/4 Sl+ 0:00  0 8100 581943 **8724** 0.8 /usr/local/node-v0.8.12/bin/node logserverdemo.js 

[[email protected] ~]# pmap 20658 
20658: /usr/local/node-v0.8.12/bin/node logserverdemo.js  
0000000000400000 8104K r-x-- /usr/local/node-v0.8.12/bin/node  
0000000000de9000  76K rwx-- /usr/local/node-v0.8.12/bin/node  
0000000000dfc000  40K rwx-- [ anon ]  
**000000001408a000 960K rwx-- [ anon ]**  
0000000040622000  4K ----- [ anon ] 

は、トラフィックが停止した後、メモリは約30分のように見えるものです。

[[email protected] ~]# ps vwwwp 20658 
    PID TTY  STAT TIME MAJFL TRS DRS RSS %MEM COMMAND 
20658 pts/4 Sl+ 0:01  0 8100 665839 **89368** 8.7 /usr/local/node-v0.8.12/bin/node logserverdemo.js 

[[email protected] ~]# pmap 20658 
20658: /usr/local/node-v0.8.12/bin/node logserverdemo.js 

0000000000400000 8104K r-x-- /usr/local/node-v0.8.12/bin/node 
0000000000de9000  76K rwx-- /usr/local/node-v0.8.12/bin/node  
0000000000dfc000  40K rwx-- [ anon ]  
**000000001408a000 80760K rwx-- [ anon ]** 
0000000040622000  4K ----- [ anon ] 
0000000040623000  64K rwx-- [ anon ] 

dataflood.rbは、データの78198894バイトの合計を書いて、リークが非常に近いです(すべてのGCが発生するため、私は待っていました)。メモリを0x1408a000にダンプし、dataflood.rbから送信していたパケットの大部分がメモリに滞留していることがわかりました。

[[email protected] ~]# ./memoryprint 20658 0x1408a000 80760000 > 20658.txt 
[[email protected] ~]# strings 20658.txt | grep '|abcde' | wc -l 
30644 
[[email protected] ~]# strings 20658.txt | grep '|abcde' | sort | uniq | wc -l 
29638 

メモリがまだ解放されていない24時間後。誰かが私に与えることができるどんな助けも大いに感謝されるでしょう。

clientList[i] = null; 
clientList[i].end(); 

は、それが他の方法で回避すべきではありません:あなたはnullにそれらを設定した後、ソケットをINGの)

+0

私は今同じ問題を抱えています。非常に最小限のコードを持つ私のプロキシサーバーは漏れており、プロファイリングは何も表示していません。 – majidarif

答えて

0

はおそらく漏れの原因ではないが、あなたは(終わりですか?

0

この問題は、入力ストリームと出力ストリームの間の速度の不均衡のために発生します。

下記のソースコードを変更してください。

<AS-IS> 

server.on('data', function(data) { 
       for(var i=0;i<clientList.length;i+=1) { 
        try { 
         clientList[i].write(data); 
        } catch (err) { 
         console.log('Error writing to client "data event": ' + clientIPList[i]); 
         // close and null the socket on write error 
         try { 
          clientList[i] = null; 
          clientList[i].end(); 
         } catch (err) {} 
         clientList.splice(i, 1); 
         clientIPList.splice(i, 1); 
        } 
       }    
      }) 

<TO-BE> 

for(var i=0;i<clientList.length;i+=1) { 
    try { 
     server.pipe(clientList[i]); 
    } catch (err) { 
     console.log('Error writing to client "data event": ' + clientIPList[i]); 
     // close and null the socket on write error 
     try { 
      clientList[i] = null; 
      clientList[i].end(); 
     } catch (err) {} 
      clientList.splice(i, 1); 
      clientIPList.splice(i, 1); 
     } 
    }    
} 

このコードは、メモリの問題を調整します。

関連する問題