简体   繁体   English

node.js脚本和可能的内存泄漏

[英]node.js script and possible memory leak

I am writing a node.js script that takes data from one connected socket and sends it to another connected socket. 我正在编写一个node.js脚本,它从一个连接的套接字获取数据并将其发送到另一个连接的套接字。 During testing I noticed that if I disconnect and reconnect a client over and over while a server is sending large amounts of data I get a memory leak. 在测试期间,我注意到如果我在服务器发送大量数据时反复断开连接并重新连接客户端,则会出现内存泄漏。 The following is the node.js code. 以下是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 );

As far as I am aware, I am handing all critical 'net' events and I am cleaning up the sockets properly, once I detect a disconnect. 据我所知,我正在处理所有关键的“网络”事件,并且一旦检测到断开连接,我正在正确清理插座。 Here are the two scripts that I am using to test. 以下是我用来测试的两个脚本。 The first just connects and disconnects as a client over and over and the second sends data as a server. 第一个只是作为客户端反复连接和断开连接,第二个作为服务器发送数据。 I run them both simultaneously. 我同时运行它们。

condiscon.rb: Connects and disconnects after registering itself as a client "sends a newline once connected". condiscon.rb:在将自己注册为客户端“连接后发送换行符”后连接和断开连接。 I run with './condiscon.rb 1000' 我用'./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: Connects as a server and sends ~2600 byte packets of abcde with a counter. dataflood.rb:作为服务器连接,用计数器发送约2600字节的abcde数据包。 I run 'dataflood.rb 30000' 我运行'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"

Here are some of the results that I am seeing. 以下是我看到的一些结果。 When running a memory profile on logserver.js before any testing it uses about 9 megabytes of resident memory. 在任何测试之前在logserver.js上运行内存配置文件时,它使用大约9兆字节的驻留内存。 I am including a pmap to show the section of memory that the leak appears to occupy. 我正在包含一个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 ]

After running the two ruby scripts above against the logserver at the ame time here is what memory looks like about 30 minute after the traffic stops. 在ame时间对着logserver运行上面的两个ruby脚本之后,在流量停止后大约30分钟就会看到内存。 (I waited for all gc to happen) (我等待所有的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 wrote a total of 78198894 bytes of data and the leak is very close. dataflood.rb共写了78198894字节的数据并且泄漏非常接近。 I dumped the memory at 0x1408a000 and I saw that most of the packets I was sending from dataflood.rb were stuck in memory. 我将内存转储到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

after waiting 24 hours the memory still had not freed. 等了24小时后,记忆仍然没有释放。 Any help that anyone can give me would be greatly appreciated. 任何人都可以给我的任何帮助将不胜感激。

Probably not causing the leak, but you are end()ing the sockets after you set them to null: 可能不会导致泄漏,但是在将它们设置为null后,您将结束()套接字:

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

Shouldn't that be the other way around? 不应该是相反的方式吗?

This problem can occurred because of the unbalance of speed between input stream and output stream. 由于输入流和输出流之间的速度不平衡,可能会发生此问题。

Try to change the source code below. 尝试更改下面的源代码。

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

This code will adjust your memory problem. 此代码将调整您的内存问题。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM