Consistent Hashing 算法实现 - JavaScript
发布于: 2020 年 10 月 26 日
目标
测试100万数据,10个服务器,每个服务器有n个虚拟节点(n = 5, 10, 50, 100, 150, 200)
测试结果
对于100万数据,10个服务器的情况下,随着每个服务器中虚拟节点的数量的增加,存储负载均匀性增强(标准差降低),在150个虚拟节点时达到最小值,此时的负载均匀表现最佳。
Total data num: 1000000, server num: 10, nodes per server: 5, standard deviation: 36095.806687203985Total data num: 1000000, server num: 10, nodes per server: 10, standard deviation: 38127.204471348276Total data num: 1000000, server num: 10, nodes per server: 50, standard deviation: 17082.357518796987Total data num: 1000000, server num: 10, nodes per server: 100, standard deviation: 13637.17885048077Total data num: 1000000, server num: 10, nodes per server: 150, standard deviation: 5072.066896246539Total data num: 1000000, server num: 10, nodes per server: 200, standard deviation: 6572.201564164021
实现代码
/** * Implementation of Consistent Hashing * * Create 1 million key-value pair and distributed across 10 servers. Evaluate implementaion by calculating standard error with 50, 100, 150, 200 nodes per server. * */// Implementationconst crypto = require('crypto')const { performance } = require('perf_hooks')const ringSize = 2_000_000class ConsistentHashing { constructor(servers = []) { this.servers = servers // { [nodeName]: [serverName] } this.serversMap = {} // { [nodePosition]: [nodeName] } this.nodesMap = {} } addServer(name, nodesNum) { const server = new ConsistentHashingServer(name, nodesNum) server.generateNodes(this.serversMap, this.nodesMap) this.servers.push(server) } // Get server for key serverFor(key) { return this.serversMap[this.nodeFor(key)] } // Get node for key nodeFor(key) { const position = this.getPosition(key) const closestPosition = this.findClosestPosition(position, Object.keys(this.nodesMap)) return this.nodesMap[closestPosition] } getPosition(key) { const hashCode = crypto.createHash('md5').update(key).digest('hex') // Get position in the ring from has code return parseInt(hashCode, 16) % ringSize } findClosestPosition(position, arr) { let left = 0 let right = arr.length - 1 let mid while (left <= right) { mid = Math.floor((right - left) / 2 + left) if (arr[mid] < position) { left = mid + 1 } else if (arr[mid] > position) { right = mid - 1 } else { return arr[mid] } } let target = 0 if (arr[right] >= position) { target = right } else if ((right + 1 <= arr.length - 1) && arr[right + 1] >= position) { target = right + 1 } return arr[target] } evaluate(keys) { const result = {} for (let key of keys) { const serverName = this.serverFor(key) if (!result[serverName]) { result[serverName] = 0 } result[serverName] += 1 } return this.standardDeviation(Object.values(result)) } standardDeviation(array) { const n = array.length const mean = array.reduce((a, b) => a + b) / n return Math.sqrt(array.map(x => Math.pow(x - mean, 2)).reduce((a, b) => a + b) / n) }}class ConsistentHashingServer { constructor(name, nodesNum) { this.name = name this.nodesNum = nodesNum } // Generate n (nodesNum) virtual nodes distributed across the ring generateNodes(serversMap, nodesMap) { for (let i = 0; i < this.nodesNum; i++) { const nodeName = this.generateNodeName(i) while (true) { const rand = Math.floor(Math.random() * ringSize) if (!nodesMap[rand]) { serversMap[nodeName] = this.name nodesMap[rand] = nodeName break } } } } generateNodeName(i) { // Node name: <serverName>-<i> return `${this.name}-${i}` }}/** * Test */let consistentHashing = new ConsistentHashing()// Add serverslet serversNumlet nodesPerServer = 5consistentHashing.addServer('A', nodesPerServer)consistentHashing.addServer('B', nodesPerServer)consistentHashing.addServer('C', nodesPerServer)console.log(consistentHashing.serverFor('foo'))console.log(consistentHashing.serverFor('bar'))console.log(consistentHashing.serverFor('hello'))console.log(consistentHashing.nodeFor('foo'))console.log(consistentHashing.nodeFor('bar'))console.log(consistentHashing.nodeFor('hello'))console.log(consistentHashing)/** * Evaluate: return standard deviation * * Data / keys count: 1,000,000 * Server number: 10 * Node number per server: [50, 100, 150, 200] */const totalDataCount = 1_000_000serversNum = 10for (let nodesNumPerServer of [5, 10, 50, 100, 150, 200]) { evaluate(totalDataCount, serversNum, nodesNumPerServer)}function evaluate(dataNum, serversNum, nodesPerServer) { const randomKeys = generateRandomKeys(dataNum) consistentHashing = new ConsistentHashing() const serverNames = Array.from(new Array(serversNum), (_, i) => `Server-${i + 1}`) for (let name of serverNames) { consistentHashing.addServer(name, nodesPerServer) } const sd = consistentHashing.evaluate(randomKeys) console.log(`Total data num: ${dataNum}, server num: ${serversNum}, nodes per server: ${nodesPerServer}, standard deviation: ${sd}`)}// Generate random datafunction generateRandomKeys(num) { // > 900 millions possible string const stringLength = 5 let keys = [] for (let i = 0; i < num; i++) { keys.push(randomString(stringLength)) } return keys}// Generate random stringfunction randomString(length) { let result = '' let characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789' let charactersLength = characters.length for ( var i = 0; i < length; i++ ) { result += characters.charAt(Math.floor(Math.random() * charactersLength)) } return result}
划线
评论
复制
发布于: 2020 年 10 月 26 日 阅读数: 9
晨
关注
还未添加个人签名 2020.05.01 加入
还未添加个人简介
评论