-
Notifications
You must be signed in to change notification settings - Fork 66
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: support more loadbalance algorithm (#7)
- Loading branch information
Showing
18 changed files
with
497 additions
and
56 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
'use strict'; | ||
|
||
const Base = require('sdk-base'); | ||
|
||
class LoadBalancer extends Base { | ||
constructor(addressGroup) { | ||
super(); | ||
this.addressGroup = addressGroup; | ||
this.reset(); | ||
this.ready(true); | ||
} | ||
|
||
get size() { | ||
return this.addressGroup._choosedSize; | ||
} | ||
|
||
get addressList() { | ||
return this.addressGroup.addressList; | ||
} | ||
|
||
select(request) { | ||
if (this.size === 0) return null; | ||
if (this.size === 1) return this.addressList[0]; | ||
|
||
return this._doSelect(request, this.addressList); | ||
} | ||
|
||
reset() {} | ||
|
||
/* istanbul ignore next */ | ||
_doSelect() { | ||
throw new Error('not implement'); | ||
} | ||
|
||
getWeight(address) { | ||
return this.addressGroup.getWeight(address); | ||
} | ||
} | ||
|
||
module.exports = LoadBalancer; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
'use strict'; | ||
|
||
const utils = require('../utils'); | ||
const LoadBalancer = require('./base'); | ||
|
||
const NUM = 128; | ||
|
||
class ConsistentHashLoadBalancer extends LoadBalancer { | ||
reset() { | ||
this._virtualNodes = new Map(); | ||
if (this.addressList) { | ||
for (const address of this.addressList) { | ||
for (let i = 0; i < NUM / 4; i++) { | ||
const digest = this._messageDigest(`${address.host}${i}`); | ||
for (let h = 0; h < 4; h++) { | ||
const m = this._hash(digest, h); | ||
this._virtualNodes.set(m, address); | ||
} | ||
} | ||
} | ||
} | ||
this._sortKeys = Array.from(this._virtualNodes.keys()).sort(); | ||
} | ||
|
||
/* eslint-disable no-bitwise */ | ||
_hash(digest, index) { | ||
const f = ((digest[3 + index * 4] & 0xFF) << 24) | | ||
((digest[2 + index * 4] & 0xFF) << 16) | | ||
((digest[1 + index * 4] & 0xFF) << 8) | | ||
(digest[index * 4] & 0xFF); | ||
return f & 0xFFFFFFFF; | ||
} | ||
/* eslint-enable no-bitwise */ | ||
|
||
_messageDigest(value) { | ||
return utils.md5(value); | ||
} | ||
|
||
_selectForKey(hash) { | ||
const len = this._sortKeys.length; | ||
let key = this._sortKeys[0]; | ||
if (this._sortKeys[len - 1] >= hash) { | ||
for (let i = len - 1; i >= 0; i--) { | ||
if (this._sortKeys[i] < hash) { | ||
key = this._sortKeys[i + 1]; | ||
break; | ||
} | ||
} | ||
} | ||
return this._virtualNodes.get(key); | ||
} | ||
|
||
_buildKeyOfHash(request) { | ||
const args = request.args; | ||
if (!args.length) return ''; | ||
return JSON.stringify(args[0]); | ||
} | ||
|
||
_doSelect(request) { | ||
const key = this._buildKeyOfHash(request); | ||
const digest = this._messageDigest(key); | ||
return this._selectForKey(this._hash(digest, 0)); | ||
} | ||
} | ||
|
||
module.exports = ConsistentHashLoadBalancer; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
'use strict'; | ||
|
||
const assert = require('assert'); | ||
|
||
const buildinLoadBalancers = { | ||
get random() { | ||
return require('./random'); | ||
}, | ||
get roundRobin() { | ||
return require('./weight_rr'); | ||
}, | ||
get consistentHash() { | ||
return require('./consistent_hash'); | ||
}, | ||
}; | ||
|
||
module.exports = addressGroup => { | ||
let loadbalancerClass = addressGroup.loadbalancerClass; | ||
if (typeof loadbalancerClass === 'string') { | ||
loadbalancerClass = buildinLoadBalancers[loadbalancerClass]; | ||
} | ||
assert(typeof loadbalancerClass === 'function', `loadbalancerClass:${addressGroup.loadbalancerClass} invalid`); | ||
return new loadbalancerClass(addressGroup); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
'use strict'; | ||
|
||
const utility = require('utility'); | ||
const LoadBalancer = require('./base'); | ||
|
||
// 负载均衡随机算法:全部列表按权重随机选择 | ||
class RandomLoadBalancer extends LoadBalancer { | ||
_doSelect(request, addressList) { | ||
const len = addressList.length; | ||
let totalWeight = 0; | ||
let isWeightSame = true; | ||
let address; | ||
for (let i = 0; i < len; i++) { | ||
const weigit = this.getWeight(addressList[i]); | ||
totalWeight += weigit; | ||
if (isWeightSame && i > 0 && weigit !== this.getWeight(addressList[i - 1])) { | ||
isWeightSame = false; | ||
} | ||
} | ||
if (totalWeight > 0 && !isWeightSame) { | ||
// 如果权重不相同且权重大于0则按总权重数随机 | ||
let offset = utility.random(totalWeight); | ||
for (let i = 0; i < len; i++) { | ||
// 并确定随机值落在哪个片断上 | ||
offset -= this.getWeight(addressList[i]); | ||
if (offset < 0) { | ||
address = addressList[i]; | ||
break; | ||
} | ||
} | ||
} else { | ||
const index = utility.random(len); // math.randomInt(len); | ||
address = addressList[index]; | ||
} | ||
return address; | ||
} | ||
} | ||
|
||
module.exports = RandomLoadBalancer; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
'use strict'; | ||
|
||
const utility = require('utility'); | ||
const LoadBalancer = require('./base'); | ||
|
||
const DEFAULT_WEIGHT = 100; | ||
|
||
// 带权重的 Round Robin 算法 | ||
class WeightRoundRobinLoadBalancer extends LoadBalancer { | ||
reset() { | ||
this._offset = utility.random(this.size); | ||
} | ||
|
||
_rr(request, addressList) { | ||
const address = addressList[this._offset]; | ||
this._offset = (this._offset + 1) % this.size; | ||
|
||
const weight = this.getWeight(address); | ||
if (weight === DEFAULT_WEIGHT) return address; | ||
if (weight === 0) return null; | ||
|
||
const randNum = utility.random(DEFAULT_WEIGHT); | ||
return weight >= randNum ? address : null; | ||
} | ||
|
||
_doSelect(request, addressList) { | ||
let address; | ||
let count = this.size; | ||
while (count--) { | ||
address = this._rr(request, addressList); | ||
if (address) return address; | ||
} | ||
// 直接返回兜底 | ||
return addressList[this._offset]; | ||
} | ||
} | ||
|
||
module.exports = WeightRoundRobinLoadBalancer; |
Oops, something went wrong.