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