thrift-zookeeper-client
Version:
A module that integrates zookeeper with thrift interfaces with connection pooling logic to make them more robust
178 lines (166 loc) • 7.13 kB
JavaScript
var zookeeper = require('node-zookeeper-client');
var async = require('async');
var serviceClientWrapper = require("./serviceClientWrapper");
var _util = require('./util');
var _initCbQueue = {}, _clusterMap = {}, _serviceClients = {}
, _zooKeeperClient, _config = {};
var zkHelper = {};
/**
* Sets config options, like min/max/timeout of resources in a pool.
* Defaults opts are:
* <pre>
* max: 3
* min: 0
* idleTimeoutMillis: 20000(ms)
* </pre>
*
*/
zkHelper.config = function (opts) {
_config.min = opts.min || 3;
_config.max = opts.max || 0;
_config.idleTimeoutMillis = opts.idleTimeoutMillis || 20000;
_config.refreshIdle = opts.refreshIdle || true;
_config.reapIntervalMillis = opts.reapIntervalMillis || 1000;
};
/**
* This method creates thrift service client. It first checks, whether a client is already in cache or not.
* If service client is already there in cache, then the callback is called with service client as argument,
* otherwise it creates a new client.
*
*
* @param {Object} zkProps, Object containing zooKeeper server ip, port and path
* @param {String} serviceName, Name of the service to be fetched
* @param {Object} serviceClient, thrift client for the service
* @param {Function} callBack, this will be called, after creation of thrift service client.
* @param {async} async, to enable TFramed protocol
*/
zkHelper.getService = function (zkProps, serviceName, serviceClient, callBack, async) {
var _serviceClient = _serviceClients[_util.getServiceClientCacheKey(zkProps, serviceName)];
if (!!_serviceClient) {
callBack(_serviceClient);
} else {
zkHelper.createServiceClient(zkProps, serviceName, serviceClient, async, callBack);
}
};
/**
* Fetches service details hosted at the specified path.
* It also adds watcher on the node, which will be triggered when service node is either deleted, modified or more
* children are added.
* Until the children details are fetched and client is created, all the request(for the same service client) callbacks
* are queued.
*
* @param {Object} zkProps, Object containing zooKeeper server ip, port and path
* @param {String} serviceName, Name of the service to be fetched
* @param {Object} serviceClient, thrift client for the service
* @param {async} async, to enable TFramed protocol
* @param {Function} callBack
*/
zkHelper.createServiceClient = function (zkProps, serviceName, serviceClient, async, callBack) {
var _cacheKey = _util.getServiceClientCacheKey(zkProps, serviceName);
var _cbQueue = _initCbQueue[_cacheKey];
if (!!_cbQueue) {
!!callBack && _cbQueue.push(callBack);
} else {
_cbQueue = [];
_initCbQueue[_cacheKey] = _cbQueue;
!!callBack && _cbQueue.push(callBack);
callZookeeper(zkProps.zkUrl, function (zkClient) {
zkClient.getChildren(zkProps.zkPath + "/nodeStatus/" + serviceName,
zkWatcher.bind(zkClient, zkProps, serviceName, serviceClient, async),
zkProcessChildren.bind(zkClient, zkProps, serviceName, serviceClient, async)
);
});
}
};
/**
* Creates zookeeper connection client.
*
* @param {String} zkUrl, zookeeper server url
* @param {Function} callBack, this is called after connection with zookeeper is established.
*/
function callZookeeper(zkUrl, callBack) {
if (!_zooKeeperClient || (!!_zooKeeperClient.getState() && (
_zooKeeperClient.getState() === zookeeper.State.EXPIRED ||
_zooKeeperClient.getState() === zookeeper.State.AUTH_FAILED ||
_zooKeeperClient.getState() === zookeeper.State.DISCONNECTED
))) {
_zooKeeperClient = zookeeper.createClient(zkUrl);
_zooKeeperClient.on('state', function (state) {
if (state === zookeeper.State.EXPIRED || state === zookeeper.State.DISCONNECTED) {
_zooKeeperClient = undefined;
}
});
_zooKeeperClient.once("connected", function () {
callBack(_zooKeeperClient);
});
_zooKeeperClient.connect();
} else {
callBack(_zooKeeperClient);
}
}
/**
* Watched event on the service node, which is triggered when the node is either deleted, modified, created or
* children are changed.
*
* @param {Object} zkProps, Object containing zooKeeper server ip, port and path
* @param {String} serviceName, Name of the service to be fetched
* @param {Object} serviceClient, thrift client for the service
* @param {async} async, to enable TFramed protocol
* @param {Object} event, see @{zookeeper.Event}
*/
function zkWatcher(zkProps, serviceName, serviceClient, async, event) {
if (zookeeper.Event.NODE_DELETED === event || zookeeper.Event.NODE_DATA_CHANGED === event
|| zookeeper.Event.NODE_CREATED === event || zookeeper.Event.NODE_CHILDREN_CHANGED === event) {
zkHelper.createServiceClient(zkProps.zkUrl, serviceName, serviceClient, async, null);
}
}
/**
* Fetches children node for a service.
* A cluster of pool is created for the service. For each child, a pool is added to the cluster.
* Before fetching children details, if any previous cluster is drained, i.e. all the pools in the cluster
* are drained gracefully.
*
* @param {Object} zkProps, Object containing zooKeeper server ip, port and path
* @param {String} serviceName, Name of the service to be fetched
* @param {Object} serviceClient, thrift client for the service
* @param {Boolean} _async, to enable TFramed protocol
* @param {Object} err, zookeeper connection error
* @param {Array} children, all the running nodes of service
*/
function zkProcessChildren(zkProps, serviceName, serviceClient, _async, err, children) {
var _cacheKey = _util.getServiceClientCacheKey(zkProps, serviceName);
var _cluster = _clusterMap[_cacheKey];
var zkClient = this;
if (!!_cluster) {
_cluster.clear();
delete _clusterMap[_cacheKey];
}
if (!!err) {
clearCbQueue(_cacheKey, undefined, err);
return;
}
_cluster = _util.initCluster(serviceName, _config);
var _nodeDetailsFn = [];
children.forEach(function (node) {
_nodeDetailsFn.push(_util.getNodeDetails.bind(zkClient, zkProps, serviceName, node, _cluster, _async));
});
async.parallel(_nodeDetailsFn, function () {
var _serviceClientClone = serviceClientWrapper(serviceClient, _cluster);
_serviceClients[_cacheKey] = _serviceClientClone;
clearCbQueue(_cacheKey, _serviceClientClone, err);
});
}
/**
* Clears all the callbacks added to the queue.
* Callback is invoked with arguments serviceClient and err object, if any.
*
* @param {String} cacheKey, cache key for call back array.
* @param {Object} serviceClient, thrift service client
* @param {Object} err, err, if any while connecting via zookeeper
*/
function clearCbQueue(cacheKey, serviceClient, err) {
var cbQueue = _initCbQueue[cacheKey];
while (cbQueue.length != 0) cbQueue.splice(0, 1)[0](serviceClient, err);
delete _initCbQueue[cacheKey];
}
module.exports = zkHelper;