关于pomelo-rpc测试压力问题
今天,在pomelo交流群里有一个朋友提交了一个pomelo压测时的问题。根据他的反馈:平时正常测试的时候,不会有任何问题,做压测时。开始出现pomelo-rpc超时问题。
从错误显示来看,这是一个请求等待应答超时的问题。
确实通过了解mqtt-mainbox.js文件的代码查看,发现每次消息发送之前协议中都会定义一个定时器用来做超时,当超过时间没有应答就会报以上的错误。
这里附加我查看过的协议代码:
MailBox.prototype.send = function(tracer, msg, opts, cb) {
tracer && tracer.info('client', __filename, 'send', 'mqtt-mailbox try to send');
if (!this.connected) {
tracer && tracer.error('client', __filename, 'send', 'mqtt-mailbox not init');
cb(tracer, new Error(this.serverId + ' mqtt-mailbox is not init ' + this.id));
return;
}
if (this.closed) {
tracer && tracer.error('client', __filename, 'send', 'mailbox has already closed');
cb(tracer, new Error(this.serverId + ' mqtt-mailbox has already closed ' + this.id));
return;
}
var id = this.curId++;
this.requests[id] = cb;
setCbTimeout(this, id, tracer, cb);
var pkg;
if (tracer && tracer.isEnabled) {
pkg = {
traceId: tracer.id,
seqId: tracer.seq,
source: tracer.source,
remote: tracer.remote,
id: id,
msg: msg
};
} else {
pkg = {
id: id,
msg: msg
};
}
if (this.bufferMsg) {
enqueue(this, pkg);
} else {
doSend(this.socket, pkg);
}
};
那为什么会超时呢?
首先我想可能是防火墙为开放相应的端口号,但是协议属于长连接,并未报链接错误。因此不是消息发送不到的问题。
之后我觉得我可能是请求过多,io阻塞导致的超时问题。这样解释的话好像就合理了。因此,我为mqtt-mailbox协议启动了bufferMsg功能,请求缓存的功能。然后交给我的朋友去测试,之后显示就正常了。看来好像确实是这个问题。
但是,当我问到压测玩家连接的时候,他说每秒15用户左右的客户端请求操作,感觉有点懵逼,为什么rpc值能支撑这么点请求呢?通过询问,我才知道原来朋友他的代码中包含了数据库io操作,并且需要等待数据库操作完成返回,才会做协议应答,而mqtt协议在发出请求的时候就已经定时,如果数据库请求时间过长就会导致协议超时。就会有上图错误。
但是问题来了,那为什么我开启bufferMsg(缓存请求)功能之后,超时问题就不再出现。我想了下,觉得应该就是bufferMsg减少了每秒对服务器的请求,同时也减少了数据库iO压力,才能使得问题不再出现,但是如果在继续加大请求力度,我估计还是会出现问题的。
那么要处理这个问题,首先一点,我们应该减少我们代码中的操作等待时间,比方说,数据库IO等待,如果减少这些东西,那么久能有效缩短协议的应答时间。然后,加长协议超时的时间,这个也是能够避免超市问题的出现的。
好了,问题讲完在将今天看的mqtt-mailbox协议源码,当我们设置了bufferMsg时候,mqtt-mailbox协议并不会把我们的请求立刻发送出去,而是先保存在了一个quque中
var enqueue = function(mailbox, msg) {
mailbox.queue.push(msg);
};
var flush = function(mailbox) {
if (mailbox.closed || !mailbox.queue.length) {
return;
}
doSend(mailbox.socket, mailbox.queue);
mailbox.queue = [];
};
var doSend = function(socket, msg) {
socket.publish({
topic: 'rpc',
payload: JSON.stringify(msg)
});
}
然后,在设定的时间间隔内,将信息一次性发出去。这样的,在大量并发请求的情况下能有效的避免请求阻塞问题。