const Consts = require("./consts"); const Scheduler = require("./scheduler"); /** * 负载均衡器 * @intro 参考electron-re项目,并做了一些改动 * @since 1.0.0 */ class LoadBalancer { static Algorithm = Consts; /** * @param {Object} options * @param {Array } options.targets [ targets for load balancing calculation: [{id: 1, weight: 1}, {id: 2, weight: 2}] ] * @param {String} options.algorithm */ constructor(options) { this.targets = options.targets; this.algorithm = options.algorithm || Consts.polling; this.params = { // data for algorithm currentIndex: 0, // index weightIndex: 0, // index for weight alogrithm weightTotal: 0, // total weight connectionsMap: {}, // connections of each target cpuOccupancyMap: {}, // cpu occupancy of each target memoryOccupancyMap: {}, // cpu occupancy of each target }; this.scheduler = new Scheduler(this.algorithm); this.memoParams = this.memorizedParams(); this.calculateWeightIndex(); } /** * 算法参数 */ memorizedParams() { return { [Consts.random]: () => [], [Consts.polling]: () => [this.params.currentIndex, this.params], [Consts.weights]: () => [this.params.weightTotal, this.params], [Consts.specify]: (id) => [id], [Consts.weightsRandom]: () => [this.params.weightTotal], [Consts.weightsPolling]: () => [this.params.weightIndex, this.params.weightTotal, this.params], [Consts.minimumConnection]: () => [this.params.connectionsMap], [Consts.weightsMinimumConnection]: () => [this.params.weightTotal, this.params.connectionsMap, this.params], }; } /** * 刷新参数 */ refreshParams(pidMap) { const infos = Object.values(pidMap); for (let info of infos) { // this.params.connectionsMap[id] = connections; this.params.cpuOccupancyMap[info.pid] = info.cpu; this.params.memoryOccupancyMap[info.pid] = info.memory; } } /** * 选举出一个进程 */ pickOne(...params) { return this.scheduler.calculate( this.targets, this.memoParams[this.algorithm](...params) ); } /** * 选举出多个进程 */ pickMulti(count = 1, ...params) { return new Array(count).fill().map( () => this.pickOne(...params) ); } /** * 计算权重 */ calculateWeightIndex() { this.params.weightTotal = this.targets.reduce((total, cur) => total + (cur.weight || 0), 0); if (this.params.weightIndex > this.params.weightTotal) { this.params.weightIndex = this.params.weightTotal; } } /** * 计算索引 */ calculateIndex() { if (this.params.currentIndex >= this.targets.length) { this.params.currentIndex = (this.params.currentIndex - 1 >= 0) ? (this.params.currentIndex - 1) : 0; } } /** * 清除data */ clean(id) { if (id) { delete this.params.connectionsMap[id]; delete this.params.cpuOccupancyMap[id]; delete this.params.memoryOccupancyMap[id]; } else { this.params = { currentIndex: 0, connectionsMap: {}, cpuOccupancyMap: {}, memoryOccupancyMap: {}, }; } } /** * 添加一个进程信息 */ add(task) { if (this.targets.find(target => target.id === task.id)) { return console.warn(`Add Operation: the task ${task.id} already exists.`); } this.targets.push(task); this.calculateWeightIndex(); } /** * 删除一个进程信息 */ del(target) { let found = false; for (let i = 0; i < this.targets.length; i++) { if (this.targets[i].id === target.id) { this.targets.splice(i, 1); this.clean(target.id); this.calculateIndex(); found = true; break; } } if (found) { this.calculateWeightIndex(); } else { console.warn(`Del Operation: the task ${target.id} is not found.`, this.targets); } } /** * 擦除 */ wipe() { this.targets = []; this.calculateWeightIndex(); this.clean(); } /** * 更新计算参数 */ updateParams(object) { Object.entries(object).map(([key, value]) => { if (key in this.params) { this.params[key] = value; } }); } /** * 设置targets */ setTargets(targets) { const targetsMap = targets.reduce((total, cur) => { total[cur.id] = 1; return total; }, {}); this.targets.forEach(target => { if (!(target.id in targetsMap)) { this.clean(target.id); this.calculateIndex(); } }); this.targets = targets; this.calculateWeightIndex(); } /** * 设置算法 */ setAlgorithm = (algorithm) => { if (algorithm in Consts) { this.algorithm = algorithm; this.params.weightIndex = 0; this.scheduler.setAlgorithm(this.algorithm); } else { throw new Error(`Invalid algorithm: ${algorithm}, pick from ${Object.keys(Consts).join('|')}`); } } } module.exports = LoadBalancer;