UNPKG

illyria

Version:

The next generation Illyria RPC SDK for node.js.

281 lines (236 loc) 7.83 kB
/** * XadillaX created at 2015-03-17 13:54:16 * * Copyright (c) 2015 Huaban.com, all rights * reserved */ var Enum = require("enum"); var EventEmitter = require("events").EventEmitter; var util = require("util"); var async = require("async"); var zookeeper = require("node-zookeeper-client"); var helper = require("./helper"); var CREATE_MODE = zookeeper.CreateMode; var ZK_STATUS = new Enum([ "NOT_CONNECTED", "CONNECTING", "CONNECTED" ]); /** * illyria zookeeper wrapper * @param {String|Array} connectString the connect string(s) * @param {Object} options the connect options, refer to * https://github.com/alexguan/node-zookeeper-client * #client-createclientconnectionstring-options * @param {String} [root] the root path * @param {String} [prefix] the path's prefix * @constructor */ var Zookeeper = function(connectString, options, root, prefix) { EventEmitter.call(this); if(util.isArray(connectString)) { connectString = connectString.join(","); } this.connectString = connectString; this.options = options; this.root = root || "/illyria"; this.prefix = prefix || "/HB_"; this.path = null; this.root = this.root.trim(); this.prefix = this.prefix.trim(); if(!this.root.length) this.root = "/illyria"; if(!this.prefix.length) this.prefix = "/HB_"; if(this.root[0] !== "/") this.root = "/" + this.root; if(this.prefix[0] !== "/") this.prefix = "/" + this.prefix; this.client = zookeeper.createClient(this.connectString, options); this.server = { host: "127.0.0.1", port: 3721, clients: 0 }; this.clientStatus = ZK_STATUS.NOT_CONNECTED; this._setup(); }; util.inherits(Zookeeper, EventEmitter); /** * setup for this zookeeper wrapper (like event listeners) * @private */ Zookeeper.prototype._setup = function() { var self = this; // automatically reconnect when expired this.client.on("state", function(state) { if(self.clientStatus !== ZK_STATUS.NOT_CONNECTED && state === zookeeper.State.EXPIRED) { setTimeout(self.connect, 100, function(err) { console.error( "Illyria zookeeper failed to reconnect when expired: " + err.message); }); } }); // automatically reconnect when dropped this.client.on("disconnected", function() { // this means disconnected manually if(self.clientStatus === ZK_STATUS.NOT_CONNECTED) { return; } setTimeout(self.connect, 100, function(err) { console.error( "Illyria zookeeper failed to reconnect when dropped: " + err.message); }); }); }; /** * set illyria server's information * @param {String} host the host of illyria server * @param {Number} port the port of illyria server */ Zookeeper.prototype.setServerInformation = function(host, port) { this.server.host = host; this.server.port = port; this._update(); }; /** * connect to zookeeper server * @param {Function} [callback] the callback function */ Zookeeper.prototype.connect = function(callback) { if(undefined === callback) callback = helper.emptyFunc; var self = this; if(this.clientStatus !== ZK_STATUS.NOT_CONNECTED) { return process.nextTick(function() { callback(new Error( "Illyria zookeeper client is already connecting or connected")); }); } this.client.once("connected", function() { self.clientStatus = ZK_STATUS.CONNECTED; self._register(callback); }); this.clientStatus = ZK_STATUS.CONNECTING; this.client.connect(); }; /** * disconnect from zookeeper server * @param {Function} [callback] the callback function */ Zookeeper.prototype.disconnect = function(callback) { if(undefined === callback) callback = helper.emptyFunc; var self = this; if(this.clientStatus !== ZK_STATUS.CONNECTED) { return process.nextTick(function() { callback(); }); } this.clientStatus = ZK_STATUS.NOT_CONNECTED; this.client.remove(self.path, function() { self.client.close(); callback(); }); }; /** * increase illyria server's connected client count in zookeeper's value * @param {Function} [callback] the callback function */ Zookeeper.prototype.incClientCount = function(callback) { this.server.clients++; this._update(callback); }; /** * decrease illyria server's connected client count in zookeeper's value * @param {Function} [callback] the callback function */ Zookeeper.prototype.decClientCount = function(callback) { this.server.clients--; if(this.server.clients < 0) this.server.clients = 0; this._update(callback); }; /** * update zookeeper's value for this node * @param {Function} [callback] the callback function * @private */ Zookeeper.prototype._update = function(callback) { if(undefined === callback) callback = helper.emptyFunc; if(this.clientStatus !== ZK_STATUS.CONNECTED) return callback(); var data = new Buffer([ this.server.host, this.server.port, this.server.clients ].join(",")); this.client.setData(this.path, data, function(err, stat) { callback(err, stat); }); }; /** * update illyria server's connected client count * @param {Number} count illyria server's connected client count * @param {Function} [callback] the callback function */ Zookeeper.prototype.updateClientCount = function(count, callback) { this.server.clients = count; this._update(callback); }; /** * register information into zookeeper for this node * @param {Function} [callback] the callback function * @private */ Zookeeper.prototype._register = function(callback) { if(undefined === callback) callback = helper.emptyFunc; if(this.clientStatus !== ZK_STATUS.CONNECTED) { return process.nextTick(function() { callback(new Error( "You must connect to Zookeeper before you register for this node.")); }); } var path = this.root + this.prefix; var client = this.client; var self = this; /** * we should create root, path first if it's not existing. * and then we insert the data to the path, * or we should just update it. */ async.waterfall([ function(callback) { client.exists(self.root, callback); }, function(exists, callback) { return exists ? callback(undefined) : client.mkdirp(self.root, function(err) { callback(err); }); }, function(callback) { var data = new Buffer([ self.server.host, self.server.port, self.server.clients ].join(",")); if(null === self.path) { client.create(path, data, CREATE_MODE.EPHEMERAL_SEQUENTIAL, callback); } else { client.exists(self.path, function(err, exists) { if(err) return callback(err); if(!exists) { return client.create(self.path, data, CREATE_MODE.EPHEMERAL, callback); } client.setData(self.path, data, function(err) { if(err) return callback(err); callback(undefined, self.path); }); }); } } ], function(err, path) { if(err) return callback(err); self.path = path; callback(err, path); }); }; Zookeeper.ZK_STATUS = ZK_STATUS; module.exports = Zookeeper;