@qiniu/miku-delivery-mp-ks
Version:
Kuaishou Mini Program SDK for Miku Delivery
145 lines (144 loc) • 4.62 kB
JavaScript
/**
* @file Consistent Hashing Tool
* @desc 用于节点选择时注意保持不同端逻辑的一致
*/
import murmur332 from './murmur3-32/index';
export default class ConsistentHash {
constructor(
/** Calculate hash for given key */
hasher = defaultHash) {
Object.defineProperty(this, "hasher", {
enumerable: true,
configurable: true,
writable: true,
value: hasher
});
/** Map: hash -> member */
Object.defineProperty(this, "circle", {
enumerable: true,
configurable: true,
writable: true,
value: new Map()
});
/** All member (key)s */
Object.defineProperty(this, "members", {
enumerable: true,
configurable: true,
writable: true,
value: new Set()
});
/** Map: member (key) -> replicas */
Object.defineProperty(this, "membersReplicas", {
enumerable: true,
configurable: true,
writable: true,
value: new Map()
});
/** Sorted hashes, small to big */
Object.defineProperty(this, "sortedHashes", {
enumerable: true,
configurable: true,
writable: true,
value: []
});
}
updateSortedHashes() {
this.sortedHashes = [...this.circle.keys()].sort((v1, v2) => v1 - v2);
}
_add({ key, replicas }) {
for (let i = 0; i < replicas; i++) {
const hash = this.hasher(getKey(key, i));
this.circle.set(hash, key);
}
this.members.add(key);
this.membersReplicas.set(key, replicas);
}
_remove({ key, replicas }) {
for (let i = 0; i < replicas; i++) {
const hash = this.hasher(getKey(key, i));
this.circle.delete(hash);
}
this.members.delete(key);
this.membersReplicas.delete(key);
}
/** Search in sortedHashes for the nearest hash of given hash value, returns its index */
search(hash) {
const i = this.sortedHashes.findIndex(h => h > hash);
return i < 0 ? 0 : i;
}
/** Get a member for given key. Returns null if no member exists. */
get(key) {
if (this.members.size === 0)
return null;
const hash = this.hasher(key);
const resultHashIdx = this.search(hash);
const resultHash = this.sortedHashes[resultHashIdx];
return this.circle.get(resultHash);
}
/** Get N members for given key. Notice: availableMembers must be a subset of members; there may not be enough members. */
getN(key, n, availableMembers) {
const size = availableMembers ? availableMembers.size : this.members.size;
if (size === 0 || n === 0)
return [];
if (n > size)
n = size;
const hash = this.hasher(key);
const start = this.search(hash);
const results = [];
for (let i = start;; i++) {
if (i >= this.sortedHashes.length)
i = 0;
const resultHash = this.sortedHashes[i];
const memberKey = this.circle.get(resultHash);
if (!results.includes(memberKey) && (!availableMembers || availableMembers.has(memberKey))) {
results.push(memberKey);
if (results.length >= n)
return results;
}
}
}
/**
* Add a member.
* - return `true` if added
* - return `false` if already exists
*/
add(key, replicas) {
if (this.members.has(key))
return false;
this._add({ key, replicas });
this.updateSortedHashes();
return true;
}
/**
* Remove given member.
* - return `true` if removed
* - return `false` if not exist
*/
remove(key) {
if (!this.members.has(key))
return false;
const replicas = this.membersReplicas.get(key);
this._remove({ key, replicas });
this.updateSortedHashes();
return true;
}
/** Sets all members */
set(members) {
this.circle.clear();
this.members.clear();
this.membersReplicas.clear();
members.forEach(member => {
this._add(member);
});
this.updateSortedHashes();
}
}
function getKey(memberKey, index) {
return index + memberKey;
}
const defaultHash = murmur3Hash32;
/** Murmur3Hash 32bit */
export function murmur3Hash32(key) {
const ab = murmur332(key);
return new Uint32Array(ab)[0];
}