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

标签 node.js memory-leaks

我正在编写一个 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 );

据我所知,我正在处理所有关键的“网络”事件,并且在检测到断开连接后正确清理套接字。这是我用来测试的两个脚本。第一个只是作为客户端反复连接和断开连接,第二个作为服务器发送数据。我同时运行它们。

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"

以下是我看到的一些结果。在任何测试之前在 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 ]

在同一时间对日志服务器运行上面的两个 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 小时后,内存仍未释放。任何人都可以给我的任何帮助将不胜感激。

最佳答案

可能不会导致泄漏,但在将套接字设置为 null 后,您正在结束()处理套接字:

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

不应该反过来吗?

关于node.js 脚本和可能的内存泄漏,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13078212/

相关文章:

node.js - 将 Elastic Beanstalk 上的 Web 服务器与 MongoDB Atlas 上的数据库服务器连接时出错

node.js - 检测nodejs中的内存泄漏

javascript - 设置 this.constructor = this 会导致循环引用/内存泄漏

objective-c - initWithContentRect 和 setContentView 中的内存泄漏

node.js - SEO与单页应用程序

Node 中的 JavaScript 字符串比较失败

windows - !heap -s 在 windbg 中不显示不断增长的堆

unit-testing - 遵循 TDD 时如何处理内存泄漏

javascript - react 组件状态内存泄漏

node.js - 使用 supertest 和 co 在请求后验证数据库内容