想问一下 nodejs 内存要怎么查看
发布于 12 年前 作者 t42dw 17443 次浏览 最后一次编辑是 8 年前

最近在用socket.io写消息中心…压测的时候如果出现当大量客户端同时继开时cpu负载升高且内存不回收造成服务异常,所以我想看一下内存中到底有些什么…那位大侠有方法?

18 回复
process.memoryUsage()

这个可以吧

大量客户端同断开、内存不回收,不光是node.js的问题,对系统tcp栈的压力也很大,用netstat -s | grep -E '.(queue|timeout).'或者dmesg**看一下有没有什么异常?node.js内存没释放可能是因为底层的tcp的连接还没有来得及正常关闭。要看内存情况的话可以用node-inspector,不过只能看heap,而且,说实话我看不大懂那东西。

另外,我也在研究长连接、高并发,欢迎一起讨论。

我是想知道内存里有些什么…不是使用情况

node --trace-gc c.js 或者你可以用https://github.com/c4milo/node-webkit-agent

但是那些都这能看到v8的heap

https://github.com/joyent/node/issues/4524

我遇到了同样的问题,在github上提问一下。你可以去看一下,但是感觉没有真正的结局

你说的tcp异常一般是指的什么呢?

我的内存没释放是一直的… 几小时都不会释放

node-inspector 这个我去看看… 我现在找不到问题的源头只能想办法知道什么没有释放,我的消息是从redis中推送过来然后我在推送给客户端的,我现在观察当大量客户端同时断开时cpu飚高然后服务就会开始异常一会,等cpu稳定后如果我再从redis中推消息给服务时cpu还是会飚高且内存会慢慢升高(这时服务以没有客户端了…),我自己试过新开服务并在没有客户端的时候给服务推送消息是不会有上面的情况

能缓解的办法就是不要用String,而是用buffer

你说的同样问题是指点内存没GC是吧…?

@t42dw 跟tcp连接断开时的4次握手有关,我推测大量客户端同时断开时,服务器上会有大量tcp连接长时间处于CLOSE_WAIT状态,用我上面给的命令可以看到存在很多queue溢出或者超时的情况……你所说的大量客户端大约是多少?

@inetfuture 测试3000客户端,1500或是3000一起断开

@inetfuture 我自己是2机器,6个核,6服务集群

@t42dw 好吧,这个量不算大,不应该是tcp的问题,断开后你用netstat -a | grep CLOSE | wc -l在服务器试下,看一下数值有多少?

var net=require(“net”) max=100000 count=0 buf = new Buffer(16*1024).toString() client=net.createConnection(7888,function(){ write() })

function write(){ client.write(buf,function(){ count++ if(count==max){ console.log(“e”) return; } write() })

}

setTimeout(function(){ console.log(1) },1000000000)

client.write(buf,function(){ count++ if(count==max){ console.log(“e”) return; } write() })

这么写就没有内存消耗了

@inetfuture 我刚测试了一下…峰值最高就是500多…然后就持续下降…等cpu稳定后就为0 …过程大概在3到5分钟

@inetfuture 整个过程cpu基本都是100%…这个时候如果再往服务推消息就会各种问题…包括内存不GC和socket.io的客户端无法coles(就是客户端断开了但服务中还存在客户端的对象正常应该会删除掉)

@t42dw 嗯,排除tcp的问题,可能你代码写的有问题,可以贴一下吗

@inetfuture 我发现内存不GC的机器上用你那命令会一直出现值…几十个到几个的样子

@inetfuture 这代码真是不好贴呀…贴出来怪怪的

var express=require(‘express’) ,app = express() , sio=require(‘socket.io’) , http = require(‘http’).createServer(app) , io = sio.listen(http) ,amqp = require(‘amqp’) ,fs = require(‘fs’) ,RedisStore = sio.RedisStore;

require(‘date-utils’);

var config=require(’./config.js’), rabbitConnectionFactory=require(’./rabbitConnectionFactory.js’), redisConnectionFactory=require(’./redisConnectionFactory.js’), log=require(’./logger.js’).getLogger(“system”), SocketManage=require(’./socketMemoryManage.js’).SocketManage, socketManage=new SocketManage(), cookieUtil=require(’./cookieUtil.js’);

var port = process.argv[2] || config.socket.port , appId = process.argv[3] || 0; global.appId=appId;

var redisPub = redisConnectionFactory.getConnectionToDB(config.redis.socketDB); var redisSub = redisConnectionFactory.getConnectionToDB(config.redis.socketDB); var redisClient = redisConnectionFactory.getConnectionToDB(config.redis.socketDB);

//当redisSub出现error时 redisSub.on(“error”,function (){ log.error(“socket.io redisSub error,exit”); process.exit(1); });

io.configure(function () { //使用redis存储会话 io.set(‘store’, new RedisStore({ redisPub:redisPub, redisSub:redisSub, redisClient:redisClient } )); });

//定义web共享环境 app.configure(function(){ //设置静态文件目录 app.use(express.static(__dirname+’/client’)); });

//创建webscoket监听服务器 http.listen(port);

//set log level io.set(“log level”, config.socket.logLevel);

var msgSubClient = redisConnectionFactory.getConnectionToDB(config.redis.socketDB); var msgGroupSubClient = redisConnectionFactory.getConnectionToDB(config.redis.socketDB);

var msgKey=“amsg:”,groupMsgKey=“amsg_group:”;

io.sockets.on(‘connection’,function(socket){ log.debug(socket.id+":connection “+appId); //注册 socket.on(‘register’,function (data){ log.debug(socket.id+” register “+appId+”,sessionId:"+data.sessionId+" groupFlag:"+data.groupFlag); if(data.sessionId){ socketManage.addIdMap(data.sessionId,socket.id); } if(data.groupFlag){ if(!socket.groupFlag){ socket.groupFlag=[]; } socket.groupFlag.push(data.groupFlag); socketManage.addGroupItem(data.groupFlag,socket.id); } //订阅消息 msgSubClient.subscribe(msgKey+socket.id); }); //关闭时清除连接的注册信息 socket.on(‘disconnect’,function(){ log.debug(socket.id+":disconnect "+appId); if(socket.groupFlag){ for(var i=0;i<socket.groupFlag.length;i++){ socketManage.removeGroupItem(socket.groupFlag[i],socket.id); } } socketManage.removeIdMap(socket.id); //取消消息订阅 msgSubClient.unsubscribe(msgKey+socket.id); }); });

//socket group 新建 socketManage.on(“group_new”,function (groupFlag){ log.debug(groupFlag+" group_new subscribe message"); msgGroupSubClient.subscribe(groupMsgKey+groupFlag); });

//socket group 已空 socketManage.on(“group_null”,function (groupFlag){ log.debug(groupFlag+" group_null unsubscribe message"); msgGroupSubClient.unsubscribe(groupMsgKey+groupFlag); });

//redis 消息 msgSubClient.on(“message”, function (channel, message) { var oldId=channel.substring(msgKey.length,channel.length); var socket=io.sockets.sockets[oldId]; if(socket){ emitMessageToClietn(socket,JSON.parse(message)); //log.debug(appId+" emit “+oldId+” msg:"+message); } });

//redis 消息 msgGroupSubClient.on(“message”, function (channel, message) { var json=JSON.parse(message); if(appId != json.nodeId){ var groupFlag=channel.substring(groupMsgKey.length,channel.length); emitMessageGroupToClietn(groupFlag,json.msg); //log.debug(“msgGroupSubClient message group:”+groupFlag); } });

var msgPubClient = redisConnectionFactory.getConnectionToDB(config.redis.socketDB); //推送到客户端消息 function emitMessageToClietn(socket,msg){ if(msg.data){ if(typeof msg.data == “string”){ msg.data=JSON.parse(msg.data); } } if(socket){ socket.emit(‘message’, msg); } }

//推送消息给组的客户端 function emitMessageGroupToClietn(groupFlag,msg){ var items=socketManage.getGroupItme(groupFlag); if(items){ var count=0; var begin=new Date().getTime(); for(var i=0;i<items.length;i++){ var socket=io.sockets.sockets[items[i]]; if(socket){ emitMessageToClietn(socket,msg); count++; } } var end=new Date().getTime(); //log.info("emitMessageGroupToClietn “+count+” items length “+items.length+” time "+(end-begin)); } }

//推送消息 function emitMessage(oldId,msg){ var socket=io.sockets.sockets[oldId]; if(socket){ //推送到客户端消息 emitMessageToClietn(socket,msg); }else{ //推送到队列 msgPubClient.publish(msgKey+oldId,JSON.stringify(msg)); } }

//推送消息给组 function emitMessageGroup(groupFlag,msg){ //消息给其它服务 msgPubClient.publish(groupMsgKey+groupFlag,JSON.stringify({nodeId:appId,msg:msg}));

//推送给本服务上的客户
emitMessageGroupToClietn(groupFlag,msg);

}

var rabbitConnection=rabbitConnectionFactory.getConnection();

rabbitConnection.on(‘ready’, function () { var q=rabbitConnection.queue(config.rabbit.receiveQueueName, {durable:true,autoDelete:false,‘arguments’: {‘x-message-ttl’: 600000}}); // Receive messages q.subscribe({ ack: true, prefetchCount: 1 },function (json, headers, deliveryInfo, m) { try{ var socketSessionId=json.socketSessionId; var groupFlag=json.socketGroupFlag; //如有group 就按组推送 if(groupFlag && groupFlag !=""){ emitMessageGroup(groupFlag,json); } //如有socketSessionId 就按单客户推送 if(socketSessionId && socketSessionId!=""){ var oldId=socketManage.getOldId(socketSessionId); if(oldId){ emitMessage(oldId,json); } } //m.acknowledge(); //q.basicAck(deliveryInfo.deliveryTag,true); log.debug(“rabbit to node message”); }catch(e){ log.error(e); } try{ m.acknowledge(true); }catch(e){ log.error(“ack msg error:”,e); } }); });

回到顶部