公司服务治理需通过 ZK 来做服务发现,Java都被集成,有封装好的包,直接注释器调用即可。Node还需自己手搓。
1. node-zookeeper-client 包连接 ZK
官方文档:https://www.npmjs.com/package/node-zookeeper-client
安装
npm i node-zookeeper-client
2. 连接ZK
const Zookeeper = require('node-zookeeper-client');
const CONNECTION_STRING = "127.0.0.1:2181"; // 要连接的ZK地址
const OPTIONS = {
sessionTimeout: 5000
}
const zk = Zookeeper.createClient(CONNECTION_STRING, OPTIONS);
zk.on('connected', function(){
console.log("zk=====", zk);
});
//获取根节点下的子节点
zk.getChildren('/', function(error, children, stat){
if(error){
console.log(error.stack);
return;
}
console.log(children);
})
zk.connect();
3. 其他API
// 判断节点是否已存在
zk.exists('/phpnode',function(error,stat){
if(stat){
console.log("节点存在");
}else{
console.log("节点不存在");
}
})
// 创建/注册节点
zk.create('/phpnode',new Buffer('hello'),function(error,path){
console.log(path);
})
// 获取节点数据
zk.getData('/phpnode',function(error,data,stat){
console.log(data.toString());
});
//节点删除
zk.remove('/phpnode',function(error){
if(!error){
console.log('node 节点删除成功');
}
})
4. DEMO(获取服务ip+port,发起请求)
- 因为我的Node服务只需调用其他服务,无需其他服务调用我,所以无需注册ZK,直接获取服务地址即可。
- 还可以通过 js-to-java 、调用 dubbo 等方式进行更优雅的调用,可自行扩展研究
封装 zk.ts 文件,方便调用
// ZK基础信息,可改为配置或传入
export const ZK = {
clientAddress: 'localhost:2181/zk/test', // ZK地址
servicePath: '/test-service', // 服务路径
};
const zookeeper = require('node-zookeeper-client');
let zkClient: any = null;
// 获取服务ip+port
export const getZKServiceBaseUrl = (servicePath: string) => {
return new Promise((resolve, reject) => {
try {
// 防止重复连接
if (zkClient) {
disconnectZKService();
}
// 新建连接
zkClient = zookeeper.createClient(ZK.clientAddress);
// 连接后执行一次
zkClient.once('connected', async function () {
// 获取服务节点信息
const res: any = await listChildren(zkClient, servicePath);
res.msgText ? reject(res) : resolve(res);
});
zkClient.connect();
} catch (error) {
reject(error);
}
});
};
// 断开链接
export const disconnectZKService = () => {
if (zkClient) {
zkClient.close();
}
};
// 获取节点信息,取出ip+port
function listChildren(client, path) {
return new Promise((resolve, reject) => {
client.getChildren(
path,
function () {},
function (error, children) {
if (error) {
reject({ ...error, msgText: `获取ZK节点error,Path: ${path}` });
}
try {
let addressPath = path + '/';
if (children.length > 1) {
//若存在多个地址,则随机获取一个地址
addressPath += children[Math.floor(Math.random() * children.length)];
} else {
//若只有唯一地址,则获取该地址
addressPath += children[0];
}
//获取服务地址
client.getData(addressPath, function (err, serviceAddress) {
if (err) {
reject({ ...error, msgText: `获取ZK服务地址error,Stack: ${err.stack}` });
}
if (!serviceAddress) {
reject({ ...error, msgText: `ZK serviceAddress is not exist` });
}
const serviceInfo = JSON.parse(serviceAddress);
const url = serviceInfo.address + ':' + serviceInfo.port;
resolve(url);
});
} catch (error) {
reject({ ...error, msgText: `list ZK children error` });
}
}
);
});
}
使用
import { getZKServiceBaseUrl, disconnectZKService, ZK } from '../../utils/zk';
const requestBaseUrl = await getZKServiceBaseUrl(ZK.servicePath);
// 用后及时断联
disconnectZKService();
// 发起请求,这里是 egg 的请求方式
this.ctx.curl(requestBaseUrl + '/v3/list/save', {
method: 'POST',
data: {},
dataType: 'json',
headers: { 'Content-Type': 'application/json' },
})
.then((res) => {})
.catch((err) => {});
- 因为我的Node服务只需调用其他服务,无需其他服务调用我,所以无需注册ZK,直接获取服务地址即可。
- 还可以通过 js-to-java 、调用 dubbo 等方式进行更优雅的调用,可自行扩展研究。