Nodejs 想封装一个网络连接,但跑着跑着就崩了
Nodejs 想封装一个网络连接,但跑着跑着就崩了
各位大神们,我想这样子封装一个网络连接,但在被拼命发包时进程内存涨到200M左右就崩掉了,是因为闭包的问题,还是Buffer拼接,还是因为一下子收到的数据包太多一次过用for处理,还是其他问题啊~~~ 我是一只小小小小鸟。。。。。。
var net = require('net');
function buildConnection(getPacks, buildPack) {
return function(sock) {
var self = this;
this.buf = new Buffer(0);
sock.on('data', function(data) {
self.buf = Buffer.concat([self.buf, data]);
var packs = getPacks(self);
for (var i = 0; i < packs.length; i++) {
// do something with pack
}
}
}
}
}
var Connection = buildConnection(aGetPacksfunction, aBuildPackfunction);
var server = net.createserver();
server.on('connection', function(sock) {
var conn = new Connection(sock);
});
server.listen(8000);
Node.js 想封装一个网络连接,但跑着跑着就崩了
各位大神们,我在尝试封装一个网络连接时遇到了一些问题。具体来说,在进行大量数据传输时(例如拼命发包),进程的内存占用会迅速增加到200MB左右,然后程序崩溃。这可能是由于闭包问题、Buffer拼接问题,或者是由于一次性处理过多数据包导致的。希望各位大神能给我一些建议。
以下是我目前的实现代码:
var net = require('net');
function buildConnection(getPacks, buildPack) {
return function(sock) {
var self = this;
this.buf = new Buffer(0);
sock.on('data', function(data) {
self.buf = Buffer.concat([self.buf, data]);
var packs = getPacks(self);
for (var i = 0; i < packs.length; i++) {
// do something with pack
}
});
};
}
// 示例函数
function aGetPacksfunction(self) {
// 解析数据包并返回
return [];
}
function aBuildPackfunction() {
// 构建数据包
}
var Connection = buildConnection(aGetPacksfunction, aBuildPackfunction);
var server = net.createServer();
server.on('connection', function(sock) {
var conn = new Connection(sock);
});
server.listen(8000, () => {
console.log('Server is listening on port 8000');
});
问题分析与解决方案
-
Buffer 拼接问题:
Buffer.concat
方法虽然方便,但如果频繁使用可能会导致内存泄漏。每次调用Buffer.concat
都会产生一个新的 Buffer 对象,旧的 Buffer 对象如果没有被引用,则会被垃圾回收器回收。- 可以考虑在适当的时候释放
self.buf
的内容,或者使用流式处理来避免内存占用过高。
-
数据包处理问题:
- 在
for
循环中处理数据包时,如果数据量过大,可能会导致性能瓶颈或内存溢出。可以考虑将数据包处理逻辑拆分到不同的事件处理器中,或者使用异步处理方法。
- 在
-
内存泄漏:
- 确保所有事件处理器和定时器都被正确地清理,以防止内存泄漏。可以使用
process.on('exit', callback)
来检查是否有未释放的资源。
- 确保所有事件处理器和定时器都被正确地清理,以防止内存泄漏。可以使用
示例改进代码
var net = require('net');
function buildConnection(getPacks, buildPack) {
return function(sock) {
var self = this;
this.buf = new Buffer(0);
sock.on('data', function(data) {
self.buf = Buffer.concat([self.buf, data]);
process.nextTick(() => {
var packs = getPacks(self);
for (var i = 0; i < packs.length; i++) {
// do something with pack
}
});
});
sock.on('end', function() {
self.buf = null; // 清理缓冲区
});
};
}
// 示例函数
function aGetPacksfunction(self) {
// 解析数据包并返回
return [];
}
function aBuildPackfunction() {
// 构建数据包
}
var Connection = buildConnection(aGetPacksfunction, aBuildPackfunction);
var server = net.createServer();
server.on('connection', function(sock) {
var conn = new Connection(sock);
});
server.listen(8000, () => {
console.log('Server is listening on port 8000');
});
通过以上改进,可以有效减少内存占用,并提高程序的稳定性和性能。希望这些方法对大家有所帮助!
为啥没有错误信息呢?
有2个原因吧:
- v8对gc不是很积极,因为gc很昂贵。 v8对小内存对象更不积极,buffer对象是小内存,但是它hold住的数据在v8的heap外,所以会造成内存暴涨,因为buffer释放缓慢 . 你可以使用node xxx --export-gc 然后在程序里定时执行gc()。
- 你在for循环时,低层libuv一直在接收数据 这个是属于proactor模式吧。底层一直在收,你可以通过接口sock.pause() and sock.resume()去控制接收buffer。
参考: https://github.com/clowwindy/shadowsocks-nodejs 的readme,作者目前放弃了nodejs版本就是因为内存的问题
self.buf = Buffer.concat([self.buf, data]);
我很好奇,这样不是每次都在self.buf后append data,self.buf不是会越来越大吗?
除非处理完pack后,重置了self.buf。 但这样tcp上的粘包没办法处理。
这是我的demo:
Session.prototype.append = function(buf) {
if (!buf)
return
if (!this._buf) {
this._buf = buf
this._buf_len = buf.length
return
}
var left = this._buf.length - this._buf_len
if (left>=buf.length) {
buf.copy(this._buf,this._buf_len)
this._buf_len+=buf.length
return
}
this._buf = Buffer.concat([this._buf.slice(0,this._buf_len),buf])
this._buf_len+=buf.length
}
楼主,将代码用 markdown 语法标记一下
200M就跳票,好像还是比较奇怪的,还没到buffer上限
根据你的描述和提供的代码片段,崩溃可能是因为内存泄漏或数据处理不当。以下是一些可能的原因和改进措施:
- 内存泄漏:
self.buf
不断增长而没有清理,可能会导致内存泄漏。 - 数据包处理:使用
Buffer.concat
和循环处理数据包的方式可能导致性能问题,尤其是在处理大量数据时。
示例代码改进
使用流式处理数据
你可以考虑使用流式处理来避免内存泄漏,并提高处理效率。
var net = require('net');
var util = require('util');
function buildConnection(getPacks, buildPack) {
return function(sock) {
let buf = Buffer.alloc(0);
sock.on('data', function(data) {
buf = Buffer.concat([buf, data]);
let offset = 0;
while (true) {
const pack = getPacks(buf.slice(offset));
if (!pack) break;
offset += pack.length;
buildPack(pack);
}
buf = buf.slice(offset);
});
sock.on('end', () => {
if (buf.length > 0) {
console.log('End of stream, remaining buffer:', buf);
}
});
};
}
const aGetPacksFunction = (buffer) => {
// 自定义逻辑来解析数据包
return buffer.slice(0, 10); // 假设前10个字节为一个包
};
const aBuildPackFunction = (pack) => {
// 处理数据包
console.log('Processing pack:', pack.toString());
};
const Connection = buildConnection(aGetPacksFunction, aBuildPackFunction);
const server = net.createServer();
server.on('connection', function(sock) {
new Connection(sock);
});
server.listen(8000, () => {
console.log('Server listening on port 8000');
});
解释
- 流式处理:通过不断累加数据并分批处理,可以避免一次性加载大量数据导致内存问题。
- 缓冲区管理:使用
Buffer.alloc
初始化缓冲区,并在每次处理完一个包后更新缓冲区。 - 循环处理:使用
while
循环处理所有数据包,直到缓冲区中的数据无法再形成完整的包。
这些改进可以帮助减少内存泄漏的风险,并提高处理效率。希望这能解决你遇到的问题!