UNPKG

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
"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