UNPKG

@qiniu/miku-delivery-mp-ks

Version:

Kuaishou Mini Program SDK for Miku Delivery

145 lines (144 loc) 4.62 kB
/** * @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]; }