pomelo-admin1312
Version:
`pomelo-admin1312` is an admin console library for [pomelo](https://github.com/NetEase/pomelo). It provides the a series of utilities to monitor the `pomelo` server clusters.
232 lines (193 loc) • 5.65 kB
JavaScript
var logger = require('pomelo-logger').getLogger('pomelo-admin', 'MqttClient');
var EventEmitter = require('events').EventEmitter;
var constants = require('../../util/constants');
var MqttCon = require('mqtt-connection');
var Util = require('util');
var net = require('net');
var MqttClient = function(opts) {
EventEmitter.call(this);
this.clientId = 'MQTT_ADMIN_' + Date.now();
this.id = opts.id;
this.requests = {};
this.connectedTimes = 1;
this.host = null;
this.port = null;
this.socket = null;
this.lastPing = -1;
this.lastPong = -1;
this.closed = false;
this.timeoutId = null;
this.connected = false;
this.reconnectId = null;
this.timeoutFlag = false;
this.keepaliveTimer = null;
this.reconnectDelay = 0;
this.reconnectDelayMax = opts.reconnectDelayMax || constants.DEFAULT_PARAM.RECONNECT_DELAY_MAX;
this.timeout = opts.timeout || constants.DEFAULT_PARAM.TIMEOUT;
this.keepalive = opts.keepalive || constants.DEFAULT_PARAM.KEEPALIVE;
}
Util.inherits(MqttClient, EventEmitter);
MqttClient.prototype.connect = function(host, port, cb) {
cb = cb || function() {}
if (this.connected) {
return cb(new Error('MqttClient has already connected.'));
}
if (host) {
this.host = host;
} else {
host = this.host;
}
if (port) {
this.port = port;
} else {
port = this.port;
}
var self = this;
this.closed = false;
var stream = net.createConnection(this.port, this.host);
this.socket = MqttCon(stream);
// logger.info('try to connect %s %s', this.host, this.port);
this.socket.connect({
clientId: this.clientId
});
this.addTimeout();
this.socket.on('connack', function() {
if (self.connected) {
return;
}
self.connected = true;
self.setupKeepAlive();
if (self.connectedTimes++ == 1) {
self.emit('connect');
cb();
} else {
self.emit('reconnect');
}
});
this.socket.on('publish', function(pkg) {
var topic = pkg.topic;
var msg = pkg.payload.toString();
msg = JSON.parse(msg);
// logger.debug('[MqttClient] publish %s %j', topic, msg);
self.emit(topic, msg);
});
this.socket.on('close', function() {
logger.error('mqtt socket is close, remote server host: %s, port: %s', host, port);
self.onSocketClose();
});
this.socket.on('error', function(err) {
logger.error('mqtt socket is error, remote server host: %s, port: %s', host, port);
// self.emit('error', new Error('[MqttClient] socket is error, remote server ' + host + ':' + port));
self.onSocketClose();
});
this.socket.on('pingresp', function() {
self.lastPong = Date.now();
});
this.socket.on('disconnect', function() {
logger.error('mqtt socket is disconnect, remote server host: %s, port: %s', host, port);
self.emit('disconnect', self.id);
self.onSocketClose();
});
this.socket.on('timeout', function(reconnectFlag) {
if (reconnectFlag) {
self.reconnect();
} else {
self.exit();
}
})
}
MqttClient.prototype.send = function(topic, msg) {
// console.log('MqttClient send %s %j ~~~', topic, msg);
this.socket.publish({
topic: topic,
payload: JSON.stringify(msg)
})
}
MqttClient.prototype.onSocketClose = function() {
// console.log('onSocketClose ' + this.closed);
if (this.closed) {
return;
}
clearInterval(this.keepaliveTimer);
clearTimeout(this.timeoutId);
this.keepaliveTimer = null;
this.lastPing = -1;
this.lastPong = -1;
this.connected = false;
this.closed = true;
delete this.socket;
this.socket = null;
if (this.connectedTimes > 1) {
this.reconnect();
} else {
this.exit();
}
}
MqttClient.prototype.addTimeout = function(reconnectFlag) {
var self = this;
if (this.timeoutFlag) {
return;
}
this.timeoutFlag = true;
this.timeoutId = setTimeout(function() {
self.timeoutFlag = false;
logger.error('mqtt client connect %s:%d timeout %d s', self.host, self.port, self.timeout / 1000);
self.socket.emit('timeout', reconnectFlag);
}, self.timeout);
}
MqttClient.prototype.reconnect = function() {
var delay = this.reconnectDelay * 2 || constants.DEFAULT_PARAM.RECONNECT_DELAY;
if (delay > this.reconnectDelayMax) {
delay = this.reconnectDelayMax;
}
this.reconnectDelay = delay;
var self = this;
// logger.debug('[MqttClient] reconnect %d ...', delay);
this.reconnectId = setTimeout(function() {
logger.info('reconnect delay %d s', delay / 1000);
self.addTimeout(true);
self.connect();
}, delay);
}
MqttClient.prototype.setupKeepAlive = function() {
clearTimeout(this.reconnectId);
clearTimeout(this.timeoutId);
var self = this;
this.keepaliveTimer = setInterval(function() {
self.checkKeepAlive();
}, this.keepalive);
}
MqttClient.prototype.checkKeepAlive = function() {
if (this.closed) {
return;
}
var now = Date.now();
var KEEP_ALIVE_TIMEOUT = this.keepalive * 2;
if (this.lastPing > 0) {
if (this.lastPong < this.lastPing) {
if (now - this.lastPing > KEEP_ALIVE_TIMEOUT) {
logger.error('mqtt rpc client checkKeepAlive error timeout for %d', KEEP_ALIVE_TIMEOUT);
this.close();
}
} else {
this.socket.pingreq();
this.lastPing = Date.now();
}
} else {
this.socket.pingreq();
this.lastPing = Date.now();
}
}
MqttClient.prototype.disconnect = function() {
this.close();
}
MqttClient.prototype.close = function() {
this.connected = false;
this.closed = true;
this.socket.disconnect();
}
MqttClient.prototype.exit = function() {
logger.info('exit ...');
process.exit(0);
}
module.exports = MqttClient;