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

我正在编写一个node.js脚本,它从一个连接的套接字获取数据,并将其发送到另一个连接的套接字。 在testing过程中,我注意到如果在服务器发送大量数据的时候断开连接并重新连接客户端,则会发生内存泄漏。 以下是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 ); 

据我所知,我正在处理所有关键的“networking”事件,我正在清理sockets,一旦我发现断开连接。 这里是我用来testing的两个脚本。 第一个作为一个客户端连接和断开连接,第二个作为服务器发送数据。 我同时运行它们。

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:作为服务器连接,并用计数器发送〜2600字节的abcde数据包。 我运行“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" 

以下是我所看到的一些结果。 在进行任何testing之前,在logserver.js上运行内存configuration文件时,它会使用大约9 MB的常驻内存。 我包括一个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 rx-- /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 ] 

在运行上面的两个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 rx-- /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小时之后,内存仍然没有释放。 任何人都可以给我的帮助将不胜感激。

可能不会造成泄漏,但是你在将它们设置为null后终止()插槽:

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

难道这不是相反吗?

由于inputstream与输出stream之间的速度不平衡,可能会出现这个问题。

尝试改变下面的源代码。

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

此代码将调整您的内存问题。