whale-nest-nacos
Version:
If you are a nest micro service architecture, you can use the SDK, so as to realize service discovery based on nacos, distributed load balancing strategy
269 lines • 12.1 kB
JavaScript
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __rest = (this && this.__rest) || function (s, e) {
var t = {};
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
t[p] = s[p];
if (s != null && typeof Object.getOwnPropertySymbols === "function")
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
t[p[i]] = s[p[i]];
}
return t;
};
Object.defineProperty(exports, "__esModule", { value: true });
const microservices_1 = require("@nestjs/microservices");
const WhaleNacosClient_1 = require("../baseNacos/WhaleNacosClient");
const constant_1 = require("../constant");
const log_1 = require("../nacosLogger/log");
class LoadBalanceService {
constructor(_a) {
var { group, serviceName, packageName, protoPath, loadBalance } = _a, grpcOptions = __rest(_a, ["group", "serviceName", "packageName", "protoPath", "loadBalance"]);
this.connectionPool = [];
this.instancePool = [];
this.group = group || constant_1.DEFAULT_GROUP;
this.serviceName = serviceName;
this.packageName = packageName;
this.protoPath = protoPath;
this.loadBalance = loadBalance;
this.grpcOptions = grpcOptions;
this.nacosClientInstance = new WhaleNacosClient_1.WhaleNacosClient({
serverAddress: constant_1.serverAddress,
namespace: constant_1.namespace,
endpoint: '7777',
logger: log_1.logger
});
}
ready() {
return __awaiter(this, void 0, void 0, function* () {
const { nacosNamingClient, nacosConfigClient } = yield this.nacosClientInstance.ready();
this.nacosNamingClient = nacosNamingClient;
this.nacosConfigClient = nacosConfigClient;
});
}
buildConnectioPool(serviceName, packageName, protoPath) {
return __awaiter(this, void 0, void 0, function* () {
const instanceList = yield this.selectHealthyInstances(serviceName, this.group);
if (this.hasInstance) {
this.batchCreateClientGrpc(instanceList, packageName, protoPath);
}
});
}
selectHealthyInstances(serviceName, group) {
return __awaiter(this, void 0, void 0, function* () {
let healthyInstances = yield this.nacosNamingClient.selectInstances(serviceName, group);
(0, log_1.log)('服务Pods:', {
serviceName: serviceName,
pods: healthyInstances
});
const noTierInstances = healthyInstances.filter(({ metadata }) => {
return metadata.TIER_VERSION ? false : true;
});
this.setServiceClusterName(noTierInstances);
return this.formatInstanceList(noTierInstances);
});
}
// 创建连接
createClientGrpc(ip, packageName, protoPath) {
return microservices_1.ClientProxyFactory.create({
transport: microservices_1.Transport.GRPC,
options: Object.assign({ url: ip, package: packageName || this.packageName, protoPath: protoPath || this.protoPath }, Object.assign({ loader: constant_1.DEFAULT_LOADER }, this.grpcOptions)),
});
}
// 创建连接池
batchCreateClientGrpc(instanceList, packageName, protoPath) {
this.instancePool = instanceList;
this.connectionPool = instanceList.map(({ ip, weight, port }) => {
const grpcClient = this.createClientGrpc(ip, packageName, protoPath);
return {
grpcClient,
weight
};
});
return this.connectionPool;
}
// 根据负载均衡策略,选择一个实例
selectOneConnectionByLoadbalance() {
const connectionPool = this.connectionPool;
if (this.loadBalance) {
return this.loadBalance(connectionPool);
}
else {
let totalWeight = 0;
for (const connection of connectionPool) {
totalWeight += connection.weight;
}
let pos = Math.random() * totalWeight;
for (const connection of connectionPool) {
if (connection.weight) {
pos -= connection.weight;
if (pos <= 0) {
const { grpcClient } = connection;
return grpcClient;
}
}
}
// 没有健康实例,则返回配置地址创建的实例,保底方案,保证服务稳定性
return this.singleClientGrpc;
}
}
// 订阅实例变更
subscribe(serviceName, packageName, protoPath) {
const params = {
serviceName,
groupName: this.group,
clusters: constant_1.WHALE_CLUSTER_INFO // 默认订阅BFF层所在集群
};
if (this.serviceClusterName) { // 更改为实例所在集群
params.clusters = this.serviceClusterName;
}
this.nacosNamingClient.subscribe(params, (instanceList) => {
const noTierInstances = instanceList.filter(({ metadata }) => {
return metadata.TIER_VERSION ? false : true;
});
this.setServiceClusterName(noTierInstances);
this.batchCreateClientGrpc(this.formatInstanceList(noTierInstances), packageName, protoPath);
(0, log_1.log)('服务Pods订阅:', {
serviceName: serviceName,
pods: instanceList
});
});
}
// 获取跨集群服务的代理ip
getGrpcProxyConfig(packageName, protoPath) {
var _a;
return __awaiter(this, void 0, void 0, function* () {
// 订阅代理ip
this.subscribeProxyConfig();
const ip = yield ((_a = this.nacosConfigClient) === null || _a === void 0 ? void 0 : _a.getConfig(constant_1.GRPC_PROXY_SERVICE, this.serviceClusterName));
if (ip) {
this.proxyClientGrpc = this.createClientGrpc(ip, packageName, protoPath);
}
else {
throw new Error(`未找到服务名为‘${constant_1.GRPC_PROXY_SERVICE}’的配置,请确认nacos是否存在该服务配置或服务名是否正确`);
}
});
}
// 订阅代理ip配置
subscribeProxyConfig() {
var _a;
(_a = this.nacosConfigClient) === null || _a === void 0 ? void 0 : _a.subscribe({
dataId: constant_1.GRPC_PROXY_SERVICE,
group: this.serviceClusterName,
}, (content) => {
if (content) {
(0, log_1.log)('代理配置订阅:', {
dataId: constant_1.GRPC_PROXY_SERVICE,
group: this.serviceClusterName,
content
});
this.proxyClientGrpc = this.createClientGrpc(content, this.packageName, this.protoPath);
}
else {
throw new Error(`代理服务‘${constant_1.GRPC_PROXY_SERVICE}’的配置不存在,请确认`);
}
});
}
// 从配置获取ip(请求地址)
getConfig(service, packageName, protoPath) {
var _a;
return __awaiter(this, void 0, void 0, function* () {
const serviceName = service || this.serviceName;
// 订阅配置变更
this.subscribeConfig(serviceName, packageName, protoPath);
const ip = yield ((_a = this.nacosConfigClient) === null || _a === void 0 ? void 0 : _a.getConfig(serviceName, this.group));
if (ip) {
this.singleClientGrpc = this.createClientGrpc(ip, packageName, protoPath);
}
else {
throw new Error(`未找到服务名为‘${serviceName}’的配置,请确认nacos是否存在该服务配置或服务名是否正确`);
}
});
}
subscribeConfig(serviceName, packageName, protoPath, group) {
var _a;
// listen data changed
(_a = this.nacosConfigClient) === null || _a === void 0 ? void 0 : _a.subscribe({
dataId: serviceName,
group: group || this.group,
}, (content) => {
if (content) {
(0, log_1.log)(`配置订阅:`, {
dataId: serviceName,
group: group || this.group,
content
});
this.singleClientGrpc = this.createClientGrpc(content, packageName, protoPath);
}
else {
throw new Error(`服务‘${this.serviceName}’的配置不存在,请确认`);
}
});
}
// 设置serviceClusterName
setServiceClusterName(instanceList) {
if (instanceList && instanceList.length) {
this.hasInstance = true;
const clusterObj = {};
instanceList.forEach(({ clusterName }) => {
clusterObj[clusterName] = true;
});
const clusterNameList = Object.keys(clusterObj);
const clusterNameCount = clusterNameList.length;
if (constant_1.WHALE_CLUSTER_INFO) {
// 异常情况:一个服务部署多个集群
if (clusterNameCount > 1) {
console.warn(`服务${this.serviceName}同时部署在${clusterNameList}集群!`);
if (clusterNameList.includes(constant_1.WHALE_CLUSTER_INFO)) {
// 优先调用本集群
this.serviceClusterName = constant_1.WHALE_CLUSTER_INFO;
}
else {
// todo 暂不考虑
// 服务跨集群,且部署在多个集群,因无法判断该往那个集群发请求,故而读取配置建立连接
this.hasInstance = false;
console.log(`服务${this.serviceName}未在本集群部署,部署集群:${clusterNameList}`);
}
}
else if (clusterNameCount == 1) {
// 与BFF在同一个集群
if (clusterNameList[0] === constant_1.WHALE_CLUSTER_INFO) {
this.serviceClusterName = constant_1.WHALE_CLUSTER_INFO;
}
else {
// 后端服务与BFF跨集群
this.serviceClusterName = clusterNameList[0];
}
}
}
else {
console.error('环境变量 process.env.WHALE_CLUSTER_INFO 不存在!');
this.hasInstance = false;
}
}
else {
this.hasInstance = false;
}
}
formatInstanceList(instanceList) {
let pods = [...instanceList];
pods = pods.filter(({ clusterName }) => clusterName == this.serviceClusterName);
const ret = (pods || []).map(({ ip, port, weight, clusterName, metadata }) => ({
ip: `${ip}:${port}`,
weight,
clusterName,
metadata
}));
return ret;
}
}
exports.default = LoadBalanceService;
//# sourceMappingURL=LoadBalanceService.js.map