dubbo-client-zookeeper-hessian
Version:
dubbo的hessian协议、zookeeper中心客户端
537 lines (512 loc) • 24.9 kB
JavaScript
'use strict';
import url from 'url';
import events from 'events';
import fs from 'fs';
import domain from 'domain';
import querystring from 'querystring';
import zookeeper from 'node-zookeeper-client';
import parser from 'java-class-parser-generics';
import hessian from 'hessian-proxy-garbled';
import tool from './toolkit.js';
let str = '',
vider = 'providers';
let readDir = tool.P(fs.readdir);
let parser2 = tool.P(parser);
let Adubbo = false;
export default class Dubbo extends events.EventEmitter {
constructor({name = 'node_client', zk, host = 'node-host', connectTimeout = 1000, retries = 3, dubbo_version = '2.8.4', service_version = false, service_group = 'dubbo', strictString = true, ip = false, username = '', password = '', data} = {}) {
if (!zk) {
throw '必须定义zk地址!';
}
if (fs.existsSync('./interface') === false) {
throw 'interface目录不存在!';
}
super();
this._client = zookeeper.createClient(zk, {
connectTimeout: connectTimeout,
retries: retries
});
this._connectTimeout = connectTimeout;
this._name = name;
this._service_version = dubbo_version;
this._service_version = service_version;
this._service_group = service_group;
this._strictString = strictString;
this._ip = ip;
this._clientHost = host;
this._username = username;
this._password = password;
this._data = data;
this._init();
}
static one(param) {
if (Adubbo === false) {
Adubbo = new Dubbo(param);
}
return this;
}
static on(event, fn) {
if (Adubbo !== false) {
Adubbo.on(event, fn)
}
return this;
}
static get service() {
if (Adubbo !== false) return Adubbo._service;
}
static get domain() {
if (Adubbo !== false) return Adubbo._domains;
}
static invoke(clzName, methodName, args,host){
if (Adubbo !== false) return Adubbo._invoke(clzName, methodName, args,host);
}
static election(arr) {
if (Adubbo !== false) return Adubbo._election(arr);
}
async _readFile(type = 0) {
if(type < 2){
let domains = await readDir('./interface/domain');
this._domains = {};
domains.forEach((ele, index) => domains[index] = `./interface/domain/${ele}`);
domains = await parser2(domains);
Object.keys(domains).forEach(clzName => {
let fns = {};
let name = clzName.substr(clzName.lastIndexOf('.') + 1);
domains[clzName].methods.forEach((method) => {
var regs = /^get(\w*)/.exec(method.name);
if (regs !== null) {
var type = method.ret, fn;
switch (type) {
case 'java.lang.String':
fn = (value) => {
if (value === null || value === undefined || value === 'null') {
return null;
}
return tool.stringSafeChange(this._strictString, value + str);
};
break;
case 'int':
case 'float':
case 'double':
case 'long':
fn = (value) => {
if (value === '' || value === undefined || value === null) {
return 0;
}
return +value;
};
break;
case 'java.lang.Integer':
case 'java.lang.Double':
case 'java.lang.Float':
case 'java.lang.Long':
case 'java.lang.Number':
fn = (value) => {
if (value === '' || value === undefined || value === null) {
return null;
}
return +value;
};
break;
case 'java.math.BigDecimal':
fn = (value) => {
if (value === '' || value === undefined || value === null) {
return null;
} else if (typeof value === 'object') {
if (value.value === '' || value.value === undefined || value.value === null) return null;
else return {value: +value.value}
} else {
return {value: +value}
}
};
break;
case 'boolean':
fn = (value) => {
if (value === '' || value === undefined || value === null) {
return false;
} else if (typeof value === 'string') {
return value === 'true';
} else {
return value
}
};
break;
case 'java.lang.Boolean':
fn = (value) => {
if (value === '' || value === undefined) {
return null;
} else if (typeof value === 'string') {
return value === 'true';
} else {
return value
}
};
break;
default:
fn = value => value;
break;
}
fns[tool.firstLowCase(regs[1])] = fn;
}
});
this._domains[clzName] = fns;
this._domains[name] = () => this._dataCheck(false, clzName);
});
}
if(type !== 1){
let services = await readDir('./interface/service');
this._service = {};
services.forEach((ele, index) => services[index] = `./interface/service/${ele}`);
services = await parser2(services);
Object.keys(services).forEach(clzName => {
let methods = {};
let name = clzName.substr(clzName.lastIndexOf('.') + 1);
services[clzName].methods.forEach((method) => {
let fns = [];
method.args.forEach((el) => {
switch (el) {
case 'java.lang.String':
fns.push((value) => {
if (value === null || value === undefined || value === 'null') {
return null;
}
return tool.stringSafeChange(this._strictString,value + str);
});
break;
case 'int':
case 'float':
case 'double':
case 'long':
fns.push(value => +value);
break;
case 'java.lang.Integer':
case 'java.lang.Double':
case 'java.lang.Float':
case 'java.lang.Long':
case 'java.lang.Number':
fns.push((value) => {
if (value !== null && value !== undefined && value !== '') {
return +value;
}
return null;
});
break;
case 'java.math.BigDecimal':
fns.push((value) => {
if (value === '' || value === undefined || value === null) {
return null;
} else if (typeof value === 'object') {
if (value.value === '' || value.value === undefined || value.value === null) return null;
else return {value: +value.value}
} else {
return {value: +value}
}
});
break;
case 'java.lang.Boolean':
fns.push((value) => {
if (value === '' || value === undefined) {
return null;
} else if (typeof value === 'string') {
return value === 'true';
} else {
return value
}
});
break;
default:
var type_, isArray = false;
if (el.indexOf('java.util.List') === -1) {
type_ = el;
} else {
type_ = el.match(/<(.*)>/);
isArray = true;
type_ !== null && (type_ = type_[1]);
}
if (this._domains[type_] !== undefined) {
if (isArray === true) {
fns.push((value) => {
if (value !== null && value !== undefined) {
value.forEach((valuei,i) => {
if (valuei !== null && valuei !== undefined) {
value[i] = this._dataCheck(valuei, type_);
}
})
}
return value;
});
} else {
fns.push((value) => {
if (value !== null && value !== undefined) {
value = this._dataCheck(value, type_);
}
return value;
});
}
} else {
fns.push(value => value);
}
break;
}
});
methods[method.name] = async(...args) => {
if (args.length !== this._service[name][method.name].check.length) {
this.emit('error', {
host: null,
className: clzName,
methodName: method.name,
param: args,
time: 0,
error: `调用${clzName}/${method.name}参数不符,期望${this._service[name][method.name].check.length}个,传了${args.length}个!`
});
throw {
state: 101006,
status : 101006,
statusMessage : `调用${clzName}/${method.name}参数不符,期望${this._service[name][method.name].check.length}个,传了${args.length}个!`,
message: `调用${clzName}/${method.name}参数不符,期望${this._service[name][method.name].check.length}个,传了${args.length}个!`,
error: `调用${clzName}/${method.name}参数不符,期望${this._service[name][method.name].check.length}个,传了${args.length}个!`
};
}
args.forEach((value, index) => {
args[index] = this._service[name][method.name].check[index](value);
});
return await this._invoke(clzName, method.name, args);
};
methods[method.name].check = fns;
});
this._service[name] = methods;
if (this._host[clzName]) {
this.emit(name, this._host[clzName]);
}
});
}
if(type === 0){
this._file_finished = true;
this._finish_call();
}
}
async _readNode(node) {
if (node === undefined) {
let children = await this._getChildren(`/${this._service_group}`, () => this._readNode());
children.forEach(child => this._readNode(child));
this._node_finished = true;
this._finish_call();
this.emit('node');
} else {
let hosts = [];
if (this._version[node] === undefined) {
this._version[node] = [];
}
let services = await this._getChildren(`/${this._service_group}/${node}/${vider}`, () => this._readNode(node));
let consumer = this._consumer();
let name = node.substr(node.lastIndexOf('.') + 1);
consumer.query.interface = node;
consumer.host = `${this._clientHost}/${node}`;
consumer.query.timestamp = consumer.query['×tamp'] = (new Date()).getTime();
consumer.query.version = consumer.query.revision = this._service_version || str;
services.forEach((service) => {
service = decodeURIComponent(service);
let ser_param = querystring.parse(service);
if (this._service_version !== false && (ser_param['default.version'] || ser_param['version']) !== this._service_version) return;
let ser_host = url.parse(service);
if (this._ip !== false && ser_host.hostname !== this._ip) return;
hosts.findIndex(h => h === ser_host.host) === -1 && hosts.push(ser_host.host);
let version = ser_param['default.version'] || ser_param['version'];
if (this._version[node].indexOf(version) === -1) {
this._version[node].push(consumer.query.version = consumer.query.revision = version);
this._addNode(`/${this._service_group}/${node}/consumers/${encodeURIComponent(url.format(consumer))}`);
}
});
this._host[node] = hosts;
if (this._service[name]) {
this.emit(name, this._host[node]);
}
this.emit('node-' + name, this._host[node]);
}
}
_finish_call() {
if (this._file_finished === true && this._node_finished === true && this._called === false) {
this._called = true;
this.emit('all');
}
}
_consumer() {
return {
protocol: 'consumer',
slashes: 'true',
host: str,
query: {
application: this._name,
category: 'consumers',
'default.timeout': this._connectTimeout,
check: 'false',
dubbo: this._service_version,
interface: str,
revision: str,
version: str,
side: 'consumer',
timestamp: str
}
};
}
_init() {
this._host = {};
this._version = {};
this._domains = {};
this._service = {};
this._called = false;
this._client.once('connected', () => {
this._getChildren = tool.P(this._client.getChildren, this._client);
this._exists = tool.P(this._client.exists, this._client);
this._createNode = tool.P(this._client.create, this._client);
this._readNode();
}).once('disconnected', () => {
});
//fs.watch('./interface/domain', () => this._readFile(1));
//fs.watch('./interface/service', () => this._readFile(2));
this._readFile();
this._client.connect();
}
async _addNode(node) {
let stat = await this._exists(node);
if (stat) return;
this._createNode(node, 1);
}
_election(arr) {
return arr[Math.floor(Math.random() * arr.length)];
}
_invoke(clzName, methodName, args,host) {
return new Promise((resolve,reject) => {
let begin = +new Date();
if (this._host[clzName] === undefined || this._host[clzName].length === 0) {
this.emit('error', {
host: null,
className: clzName,
methodName: methodName,
param: args,
time: 0,
error: `${clzName}/${methodName}zk中找不到对应的服务节点`
});
throw {
status : 101002,
statusMessage : `${clzName}/${methodName}zk中找不到对应的服务节点`,
message: `${clzName}/${methodName}zk中找不到对应的服务节点`,
error: `${clzName}/${methodName}zk中找不到对应的服务节点`,
state: 101002
};
}
if(host === undefined){
host = this._election(this._host[clzName]);
}
let domain_ = domain.create();
domain_.on('error', (err) => {
if (err.code && err.code.toString() === 'ECONNREFUSED') {
let end = +new Date();
this.emit('error', {
host: host,
className: clzName,
methodName: methodName,
param: args,
time: end - begin,
error: `${host}/${clzName}/${methodName}连接失败!`
});
reject({
message: `${host}/${clzName}/${methodName}连接失败!`,
error: `${host}/${clzName}/${methodName}连接失败!`,
state: 101005
});
} else {
let end = +new Date();
this.emit('error', {
host: host,
className: clzName,
methodName: methodName,
param: args,
time: end - begin,
error: `${host}/${clzName}/${methodName}发生未知异常:${JSON.stringify(err)}!`
});
reject({
message: `${host}/${clzName}/${methodName}发生未知异常:${JSON.stringify(err)}!`,
error: `${host}/${clzName}/${methodName}发生未知异常:${JSON.stringify(err)}!`,
state: 101004
});
}
});
domain_.run(() => {
var proxy = new hessian.Proxy(`http://${host}/${clzName}`, this._username, this._password, proxy);
proxy.invoke(methodName, args, (err, reply) => {
if (reply && reply.fault === true) {
let end = +new Date();
this.emit('error', {
host: host,
className: clzName,
methodName: methodName,
param: args,
time: end - begin,
error: `${host}/${clzName}/${methodName}发生故障${JSON.stringify(reply)}!`
});
reject({
message: `${host}/${clzName}/${methodName}发生故障${JSON.stringify(reply)}!`,
error: `${host}/${clzName}/${methodName}发生故障${JSON.stringify(reply)}!`,
state: 101004
});
} else if (err) {
let end = +new Date();
this.emit('error', {
host: host,
className: clzName,
methodName: methodName,
param: args,
time: end - begin,
error: `${host}/${clzName}/${methodName}发生异常${JSON.stringify(err)}!`
});
reject({
message: `${host}/${clzName}/${methodName}发生异常${JSON.stringify(err)}!`,
error: `${host}/${clzName}/${methodName}发生异常${JSON.stringify(err)}!`,
state: 101004
});
} else {
let end = +new Date();
this.emit('data', {
host: host,
className: clzName,
methodName: methodName,
param: args,
time: end - begin,
data: reply
});
if (this._data) {
try{
resolve(this._data(reply));
}catch(error){
this.emit('error', {
host: host,
className: clzName,
methodName: methodName,
param: args,
time: end - begin,
error: `${host}/${clzName}/${methodName}发生异常${JSON.stringify(error)}!`
});
reject(error);
}
} else {
resolve(reply);
}
}
});
});
});
}
_dataCheck(domain, fullName) {
let fn = this._domains[fullName];
var pre = {};
if (domain === false) {
Object.keys(fn).forEach(i => pre[i] = fn[i](''))
} else {
Object.keys(domain).forEach(i => fn.hasOwnProperty(i) && (pre[i] = fn[i](domain[i])))
}
pre.__type__ = fullName;
return pre;
}
}
module.exports = exports.default;