2017-10-17更新 修复无法自动建立多级目录问题。
使用node-zookeeper-client,其它的安装不上,各种报错之后放弃。
核心功能:适用于RPC服务端 zookeeper 注册节点信息, 适用于RPC客户端获取节点信息,并另有简单负载均衡功能。
npm install node-zookeeper-client
简单说zookeeperRPC,包含如下功能注册服务端信息(znode临时节点),移除注册节点。获取服务端节点,
获取负载服务端节点。
注意:zookeeper 客户端连接时在某些环境下会出现 执行exists时报 UNIMPLEMENTED[-6] 继续执行建立节点和设置值时会连续报错,调整配置后 执行exists时报 UNIMPLEMENTED[-6]
配置调整如下
public static _option:zookeeper.Option ={
sessionTimeout:60000,
spinDelay:5000,
retries:3,
};
其它方法不报错了。具体原因不详。
zookeeperRPC
核心功能:
1)register:注册zookeeper节点信息
2)logout:注销zookeeper节点信息
3)getRegistration:获取注册的服务器信息
4)getLoadBalanceServer:获取负载均衡后的服务器地址
import { ZookeeperHelper } from '../../common/ZookeeperHelper';
import * as zookeeper from 'node-zookeeper-client';
import { Registration } from "../../Model/Registration";
var loadbalance = require('loadbalance');
//var cache = require('../Model/local-storage');
/**
* ZookeeperRPC
*
* @export
* @class ZookeeperRPC
*/
export class ZookeeperRPC {
protected _zookeeperHelper: ZookeeperHelper = new ZookeeperHelper();
protected _getLoadBalanceServerCallBack: (loadbalance?: string) => void;
protected _cache: Array<string>;
protected _path: string;
public registerTransaction() {
//cache.setItem("ROUTE_KEY", {});
}
/**
* 字符串format "sadfadf{0}vvvv{1}"
*
* @param {string} string
* @param {...any[]} args
* @returns {string}
* @memberof ZookeeperRPC
*/
public stringFormat(string: string, ...args: any[]): string {
//var args = arguments;
return string.replace(/\{(\d+)\}/g,
function (m, i) {
return args[i];
});
}
/**
* 注册服务器信息
*
* @param {string} path
* @param {Registration} reg
* @memberof zookeeperRPC
*/
public register(path: string, reg: Registration) {
this._zookeeperHelper.connect();
this._path = path;
//this._client.once("connected", callback);
this._zookeeperHelper.exists(this._path, (error, stat) => {
console.log("stat" + stat);
console.log("error" + error);
//console.log("::::::::::::::::::::::" + this._zookeeperHelper._client.getSessionId().toString('hex'));
reg.sessionid = this._zookeeperHelper._client.getSessionId().toString('hex');
var dataBuffer = new Buffer(JSON.stringify(reg));
if (stat) {
//console.log('节点存在.');
// console.log(this._path + "/" + ipport);
this._zookeeperHelper.setData(this._path, dataBuffer);
//this._zookeeperHelper.close();
} else {
//console.log('register 该节点不存在.');
//console.log('该节点不存在.' + path);
//this._path="/tttt";
this._zookeeperHelper.mkdirp(this._path, (error, path) => {
//console.log("this._path:" + this._path);
if (error) {
console.log("tttttttttt:" + error);
}
//console.log("path 123456 ::::::" + path);
this._zookeeperHelper.setData(this._path, dataBuffer);
});
// let path = "/servicecenter-dev/services/" + reg.servicenamespace + "/providers";///" + reg.ip + ":" + reg.port;
// this._zookeeperHelper.create(path, dataBuffer);
}
});
}
/**
* 注销-服务信息
*
* @param {string} path
* @memberof ZookeeperRPC
*/
public logout(path: string) {
this._zookeeperHelper.remove(path);
}
/**
* 获取注册服务信息
*
* @param {string} path
* @param {((error: Error | zookeeper.Exception, children: string[], stat: zookeeper.Stat) => void)} callback
* @memberof ZookeeperRPC
*/
public getRegistration(path: string, callback: (error: Error | zookeeper.Exception, children: string[], stat: zookeeper.Stat) => void) {
this._zookeeperHelper.getPathServices(path, callback);
}
/**
* 获取负载均衡后的服务器地址
*
* @param {string} path
* @param {((error: Error | zookeeper.Exception, children: string[], stat: zookeeper.Stat) => void)} callback
* @memberof ZookeeperRPC
*/
public getLoadBalanceServer(path: string, callback: (loadbalance?: string) => void) {
this._getLoadBalanceServerCallBack = callback;
if (this._cache) {
let engine = loadbalance.roundRobin(this._cache);
let pick = engine.pick();
callback(pick);
return;
} else {
this._zookeeperHelper.getPathServices(path, (error: Error | zookeeper.Exception, children: string[], stat: zookeeper.Stat) => {
if (error) {
return;
}
if (children.length > 0) {
//设置本地缓存和负载策略
this._cache = children;
//cache.setItem("ROUTE_KEY")[path] = loadbalance.roundRobin(children);
let engine = loadbalance.roundRobin(children);
let pick = engine.pick();
callback(pick);
/*测试
for (var index = 0; index < 100; index++) {
var pick = engine.pick();
callback(pick);
}*/
return;
}
});
}
}
}
/*
var z = new ZookeeperRPC();
var reg: Registration = {
servicenamespace: "YinXin.BaoXian.RPC.test",
sessionid: "z._client.getSessionId().toString()",
ip: "127.0.0.888",
port: 2011,
boostpercent: 10
};
z.register("/test1.1", reg);
z.register("/test1.2", reg);
*/
ZookeeperHelper
核心功能:
1)md_full_path:建立zookeeper多节点信息,如/test/BBB/CCC/DDD/EEE 会直接依次全部建立完毕。
2)其它 功能详见注释。
import { BaseZookeeper } from './BaseZookeeper';
import * as zookeeper from 'node-zookeeper-client';
import { Registration } from "../Model/Registration";
/**
* ZookeeperHelper
*
* @export
* @class ZookeeperHelper
* @extends {BaseZookeeper}
*/
export class ZookeeperHelper extends BaseZookeeper {
protected _getPathServices_callback?: (error: Error | zookeeper.Exception, children: string[], stat: zookeeper.Stat) => void;
/**
* 注册服务器信息
*
* @param {string} path
* @param {Registration} reg
* @memberof zookeeperHelper
*/
public register(path: string, reg: Registration) {
this.connect();
//this._client.once("connected", callback);
reg.sessionid = this._client.getSessionId().toString('hex');
// console.log("::::::::::::::::::::::" + this._client.getSessionId().toString('hex'));
var dataBuffer = new Buffer(JSON.stringify(reg));
this.create(path, dataBuffer);
}
/**
* 连接
*
* @memberof ZookeeperHelper
*/
public connect() {
let state = this._client.getState();
if (state == zookeeper.State.DISCONNECTED) {
//console.log("当前连接状态!" + state);
this._client.connect();
//console.log("已连接!");
let sessionTimeout = this._client.getSessionTimeout();
//console.log(sessionTimeout);
//console.log(this._client);
}
}
/**
* 关闭
*
* @memberof ZookeeperHelper
*/
public close() {
console.log("Zookeeper 连接已关闭!");
this._client.close();
}
/**
* 注册节点信息
*
* @param {string} path
* @param {(Buffer | null)} data
* @memberof zookeeperHelper
*/
public create(path: string, data: Buffer | null) {
this.connect();
if (data) {
this._client.create(path, data, (error: Error | zookeeper.Exception, path: string) => {
this.create_callback(error, path);
});
}
else {
this._client.create(path, (error: Error | zookeeper.Exception, path: string) => {
this.create_callback(error, path);
});
}
}
/**
* create 回调
*
* @param {(Error|zookeeper.Exception)} error
* @param {string} path
* @memberof zookeeperHelper
*/
public create_callback(error: Error | zookeeper.Exception, path: string) {
if (error) {
console.log("zookeeper create error:" + error);
}
}
/**
* 移除该节点
*
* @param {string} path
* @memberof zookeeperHelper
*/
public remove(path: string) {
this.connect();
this._client.remove(path, -1, (error: Error | zookeeper.Exception) => {
if (error) {
console.log("zookeeper remove error:" + error);
return;
}
console.log('Node is deleted.');
})
}
/**
* 是否存在该路径
*
* @param {string} path
* @param {((error: Error | zookeeper.Exception, stat: zookeeper.Stat) => void)} callback
* @memberof zookeeperHelper
*/
public exists(path: string, callback: (error: Error | zookeeper.Exception, stat: zookeeper.Stat) => void) {
this.connect();
this._client.exists(path, callback.bind(this));
}
public exists_callback(error: Error | zookeeper.Exception, stat: zookeeper.Stat) {
if (error) {
console.log(error);
return;
}
if (stat) {
console.log('节点存在.');
} else {
console.log('该节点不存在.');
}
console.log("exists关闭");
}
/**
* 设置数据
*
* @param {string} path
* @param {Buffer} data
* @memberof zookeeperHelper
*/
public setData(path: string, data: Buffer) {
this.connect();
//console.log("fffffffffff::::::::" + path);
this._client.setData(path, data, (error, stat) => {
//console.log("22222222222::::::::" + path);
if (error) {
console.log("zookeeper setData error:" + error);
return;
}
if (stat) {
//console.log(stat);
}
console.log("setData执行完毕");
});
}
/**
* 获取数据
*
* @param {string} path
* @memberof zookeeperHelper
*/
public getData(path: string) {
this.connect();
this._client.getData(
path,
(event) => {
console.log('Got event: %s.', event);
},
(error, data, stat) => {
if (error) {
console.log(error);
return;
}
if (stat) {
console.log(stat);
console.log(data.toString());
} else {
console.log(stat);
}
});
}
/**
* 获取zookeeper下所有节点
*
* @param {string} path
* @memberof zookeeperHelper
*/
public getPathServices(path: string, callback: (error: Error | zookeeper.Exception, children: string[], stat: zookeeper.Stat) => void) {
this.connect();
this._getPathServices_callback = callback;
this._client.getChildren(
path,
/*(event) => {
//console.log('Got Services watcher event: %s', event);
this.getPathServices(this._service_root_path);
},*/
(error, children, stat) => {
if (this._getPathServices_callback) {
this._getPathServices_callback(error, children, stat);
}
if (error) {
return;
}
console.log(children);
this._getPathServices_callback = undefined;
}
);
}
/**
* 建立路径-临时znode
*
* @param {string} path
* @param {((error: Error | zookeeper.Exception, path: string) => void)} [callback]
* @memberof ZookeeperHelper
*/
public mkdirp(path: string, callback?: (error: Error | zookeeper.Exception, path: string) => void) {
this.connect();
//this.md_full_path(path, callback);
//console.log("执行mkdir");
if (callback) {
//console.log("执行临时Znode");
this._client.mkdirp(path, zookeeper.CreateMode.EPHEMERAL, callback.bind(this));
} else {
this._client.mkdirp(path, (error, path) => {
if (error) {
console.log("zookeeper mkdirp error :" + error);
}
// console.log("mkdirp error:mkdirp:::::12345678910" + error);
});
}
}
protected _mdPath?: string[];
protected _mdIndex: number;
/**
* 建立全目录
*
* @param {string} path
* @param {((error: Error | zookeeper.Exception, path: string) => void)} [callback]
* @memberof ZookeeperHelper
*/
public md_full_path(path: string, callback?: (error: Error | zookeeper.Exception, path: string) => void) {
path = path.substring(1, path.length);
//console.log("pppp:" + path);
this._mdPath = path.split("/");
//console.log("pppccccp:" + this._mdPath);
this._mdIndex = 0;
//for (let i = 0; i < len; i++) {
this.md("/" + this._mdPath[0], callback);
}
/**
* 建立目录递归
*
* @private
* @param {string} path
* @param {((error?: Error | zookeeper.Exception, path?: string) => void)} [callback]
* @memberof ZookeeperHelper
*/
private md(path: string, callback?: (error?: Error | zookeeper.Exception, path?: string) => void) {
this.exists(path, (error: Error | zookeeper.Exception, stat: zookeeper.Stat) => {
//console.log("fdffffff:" + this._mdPath + " " + this._mdIndex + " ");
this._mdIndex++;
if (error && callback) {
//console.log("md " + error);
callback(error, undefined);
return;
} else {
//console.log("sssssssssssss ");
if (stat == undefined) {//目录不存在
//console.log(path + "目录不存在");
var creatModel = zookeeper.CreateMode.PERSISTENT;
if (this._mdPath && this._mdPath.length < this._mdIndex) {
creatModel = zookeeper.CreateMode.EPHEMERAL;
//console.log("临时目录");
//if (callback) callback(error, path);
}
this._client.mkdirp(path, creatModel, (error, path) => {
//this._client.mkdirp(path, (error, path) => {
if (error) {
console.log("zookeeper mkdirp error :" + error);
}
this.performRecursion(error, path, callback);
});
}
else {//目录存在
//console.log(path + "目录存在");
//console.log("callback" + callback)
this.performRecursion(error, path, callback);
/* var mdpath = this.get_mdpath()
if (mdpath != "") {
this.md(mdpath, callback);
return;
} else {
if (callback)
callback(error, path);
}*/
}
}
})
}
/**
*判断递归路径是否有值,如存在则执行递归。
*
* @private
* @param {(Error | zookeeper.Exception)} error
* @param {string} path
* @param {((error?: Error | zookeeper.Exception, path?: string) => void)} [callback]
* @memberof ZookeeperHelper
*/
private performRecursion(error: Error | zookeeper.Exception, path: string, callback?: (error?: Error | zookeeper.Exception, path?: string) => void) {
var mdpath = this.get_mdpath();
//console.log("performRecursion" + mdpath);
if (mdpath != "") {
this.md(mdpath, callback);
} else if (callback) {
//console.log("zhixing====================:");
callback(error, path);
}
}
/**
* 获取递归的path路径
*
* @private
* @type {string}
* @memberof ZookeeperHelper
*/
private get_mdpath() {
var mdpath: string = "";
//console.log("mdpath:" + this._mdPath)
//console.log("_mdIndex_mdIndex_mdIndex:" + this._mdIndex)
if (this._mdPath && this._mdIndex) {
mdpath = "";
let len = this._mdPath.length;
for (let i = 0; i < this._mdIndex; i++) {
mdpath += "/" + this._mdPath[i];
}
//this._mdIndex++;
if (this._mdPath) {
//console.log("fdffffff:" + " " + this._mdIndex + " " + this._mdPath.length);
if (this._mdPath.length < this._mdIndex) {
//console.log("执行BBBBBB++ " + this._mdIndex);
//console.log(this._mdPath);
return "";
}
}
//console.log("mdpath------------------:" + mdpath + " " + this._mdIndex);
}
return mdpath;
}
}
BaseZookeeper
import * as zookeeper from "node-zookeeper-client";
import { ZookeeperEvent } from "../Enum/ZookeeperEvent";
/**
* BaseZookeeper
*
* @export
* @class BaseZookeeper
*/
export class BaseZookeeper {
public static _connectionString = "192.168.145.3:2281";
public static _option: zookeeper.Option;
public _client: zookeeper.Client;
public _hosts: string = "192.168.145.3:2281,192.168.145.4:2281,192.168.145.7:228";
public _service_root_path: string = "/";
constructor() {
this._client = zookeeper.createClient(BaseZookeeper._connectionString, BaseZookeeper._option);
this.startClinetListener();
}
/**
* 创建客户端
*
* @memberof BaseZookeeper
*/
public createClinet(){
this._client = zookeeper.createClient(BaseZookeeper._connectionString, BaseZookeeper._option);
this.startClinetListener();
}
/**
* 启动客户端监听
*
* @memberof BaseZookeeper
*/
public startClinetListener() {
this._client.addListener(ZookeeperEvent.STATE, this.listenStateHandle.bind(this));
this._client.addListener(ZookeeperEvent.CONNECTED, this.listenConnectedHandle.bind(this));
this._client.addListener(ZookeeperEvent.CONNECTEDREADONLY, this.listenConnectedreadonlyHandle.bind(this));
this._client.addListener(ZookeeperEvent.DISCONNECTED, this.listenDisconnectedHandle.bind(this));
this._client.addListener(ZookeeperEvent.EXPIRED, this.listenExpiredHandle.bind(this));
this._client.addListener(ZookeeperEvent.AUTHENTICATIONFAILED, this.listenAuthenticationfailedHandle.bind(this));
}
/**
* zookeeper State 监听
*
* @param {zookeeper.State} state
* @memberof BaseZookeeper
*/
public listenStateHandle(state: zookeeper.State) {
console.log("zookeeper state:" + state);
if (state === zookeeper.State.SYNC_CONNECTED) {
console.log('Client state is changed to connected.');
}
if (state == zookeeper.State.EXPIRED) {//过期
this.createClinet();
this._client.connect();
console.log("过期重新创建!");
}
}
/**
* zookeeper Connected 监听
*
* @memberof BaseZookeeper
*/
public listenConnectedHandle() {
console.log("zookeeper Connected");
}
/**
* zookeeper Connectedreadonly 监听
*
* @memberof BaseZookeeper
*/
public listenConnectedreadonlyHandle() {
console.log("zookeeper Connectedreadonly");
}
/**
* zookeeper Disconnected 监听
*
* @memberof BaseZookeeper
*/
public listenDisconnectedHandle() {
console.log("zookeeper Disconnected");
}
/**
* zookeeper Expired 监听
*
* @memberof BaseZookeeper
*/
public listenExpiredHandle() {
console.log("zookeeper Expired");
}
/**
* zookeeper Authenticationfailed 监听
*
* @memberof BaseZookeeper
*/
public listenAuthenticationfailedHandle() {
console.log("zookeeper Authenticationfailed");
}
}
enum
export enum ZookeeperEvent {
STATE = "state",
CONNECTED = "connected",
CONNECTEDREADONLY = "connectedReadOnly",
DISCONNECTED = "disconnected",
EXPIRED = "expired",
AUTHENTICATIONFAILED = "authenticationFailed",
}