5

接続されている1つのソケットからデータを取得し、それを別の接続されているソケットに送信する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つのスクリプトです。1つ目はクライアントとして何度も接続と切断を繰り返し、2つ目はサーバーとしてデータを送信します。両方を同時に実行します。

condiscon.rb:クライアントとして登録した後、接続および切断します。「接続すると改行を送信します」。'./condiscon.rb1000'で実行します

#!/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.rb30000'を実行します

#!/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を含めています。

[root@localhost ~]# 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

[root@localhost ~]# 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 ]

上記の2つのrubyスクリプトをアメ時間にログサーバーに対して実行した後、トラフィックが停止してから約30分後のメモリは次のようになります。(私はすべてのgcが発生するのを待ちました)

[root@localhost ~]# 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

[root@localhost ~]# 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バイトのデータを書き込み、リークは非常に接近しています。0x1408a000でメモリをダンプしましたが、dataflood.rbから送信していたパケットのほとんどがメモリに残っていることがわかりました。

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

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

4

2 に答える 2

0

おそらくリークの原因ではありませんが、ソケットを null に設定した後、ソケットを end() しています。

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

それは逆であってはいけませんか?

于 2012-10-30T09:48:02.037 に答える
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);
        }
    }            
 }

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

于 2013-01-19T17:50:06.347 に答える