本文介绍了分布式哈希表的概念及实现,这种描述非常适合代码模拟实现。文末附上了实现的代码(Java版和Python版)
本文内容(除代码外)皆来自 《Foundations of Computers Systems Research》一书,自己翻译的,转载请注明出处,不准确的部分请告知,欢迎讨论。
什么是分布式哈希表?
分布式哈希表是一个基本的结构,被广泛的应用在许多分布式系统中,用于组织动态改变的(分布式)节点集合和透明的资源定位服务,如,DHT 在许多 P2P 系统中作为一个基本结构提供高效透明的资源定位服务。所有节点都同等重要,这使得 DHT 更加的装载平横。而且,在 DHT 中具有一定规律的拓扑结构(环、树、超立方体、网格、蝴蝶形……),这使得查找更加的高效。虽然,各种 DHT 实现的拓扑结构各不相同,但它们至少提供以下两个基本的功能:
DHT 网络设计的重点大多在以下几个方面:
|
各种 DHT 对比
DHTs | Topology | Node Degree | Routing Hops |
CAN | mesh | O(d) | O(dn^(1/d)) |
Chord | ring | O(logn) | O(logn) |
Pastry | tree + ring | O(logn) | O(logn) |
Tapestry | tree | O(logn) | O(logn) |
Kademlia | tree | O(logn) | O(logn) |
Viceroy | butterfly | 7 | O(logn) |
Koord | de Bruijn graph + ring | 2 or O(logn) | O(logn) or O(logn/loglogn) |
Cycloid | hypecube + ring | 3 | O(d) with n = d2^d |
一个 DHT 例子 —— Chord
学习案例(待更新)
案例一:协同域名系统(Cooperative Domain Name System,CoDoNS) |
案例二:协同文件系统(Cooperative File System,CFS) |
代码实现
Java(包含四个文件:测试类、节点类、Chord类、节点IP地址文件) ![]() 1 //只写了初始化数据结构、节点加入、节点查询 2 //没有写节点的移除,数据的存储 3 4 IPAddressList.txt 5 211.138.116.246 6 128.001.223.111 7 176.002.222.123 8 223.104.160.069 9 112.017.247.082 10 203.069.010.164 11 12 13 14 package DisturbedHashTable; 15 16 import java.io.IOException; 17 //测试类 18 public class MainTest { 19 public static void main (String[] args) throws IOException{ 20 ChordDHT dht = new ChordDHT(6, "F:\\VSCodeProjects\\JavaProjects\\DisturbedHashTable\\IPAddressList.txt"); 21 dht.printNode(); 22 23 //测试加入 24 Node node = new Node(6, "242.108.11.31"); 25 Node curNode = dht.getNode(32); 26 dht.joinToRing(curNode, node, 6); 27 28 var tmp = node.getSuccessor(); 29 System.out.printf("\n%d ", node.getIdentifier()); 30 while(tmp != node){ 31 System.out.printf("%d ", tmp.getIdentifier()); 32 tmp = tmp.getSuccessor(); 33 } 34 35 } 36 } 37 38 //Chord类 39 package DisturbedHashTable; 40 41 import java.io.IOException; 42 import java.nio.charset.StandardCharsets; 43 import java.nio.file.Path; 44 import java.util.HashMap; 45 import java.util.Scanner; 46 47 /** 48 * 基本信息: 49 * 1. Chord 是一个环状结构 50 * 2. 每个节点、键值都被映射成了 m 位的标识符 51 * 3. 支持查找、插入、移除节点操作 52 * 4. 每个节点负责存储标识符为 (i-1, i] 5. 每个节点都有一个 fingerTable 53 * 54 * 实现想法: 55 * 1. 利用双向循环链表作为环状结构 (X) 不用链表,链表不能随机访问,用一个 HashMap 56 * 2. 标识符空间设置位 6 位 57 * 3. 哈希函数将各个节点的IP地址映射成为 6 位的标识符 58 * 4. 每个节点还有一个链表存储其负责的标识符和 fingerTable 59 * 60 */ 61 62 public class ChordDHT{ 63 //节点集合,键值是其标识符 64 private HashMap
Python(都写在一个文件中,节点IP地址文件同上)
![]() 1 import random 2 import time 3 4 5 ringLenth = lambda x: int(pow(2, x)) 6 7 8 class Node: 9 ''' 哈希表中的节点数据结构 ''' 10 11 def __init__(self, ip, m): 12 self.id = self.generateId(ip = ip, m = m) 13 self.fingerTable = self.generateFT(m = m) 14 self.predecessor = self 15 self.sucessor = self 16 17 18 def generateId(self, ip, m): 19 result = 0 20 random.seed(time.time()) 21 for ch in ip: 22 result = (result + ord(ch) + random.randint(1, 99)) % ringLenth(m) 23 return result 24 25 26 def generateFT(self, m): 27 result = {} 28 for i in range(m): 29 result[int(pow(2, i))] = self 30 return result 31 32 33 def updateFT(self, m, dht): 34 for item in self.fingerTable: 35 pointer = (item + self.id) % ringLenth(m) 36 for node in dht.getNodes(): 37 if node.isInBound(pointer, m): 38 self.fingerTable[item] = node 39 break 40 41 42 def isInBound(self, pointer, m): 43 # 每个节点负责的标识符范围,有三种情况 44 # case 1 环中只有一个节点时 45 if self.id == self.predecessor.getId(): 46 return True 47 # case 2 前驱节点标识符较小 48 elif self.id > self.predecessor.getId(): 49 if pointer > self.predecessor.getId() and pointer <= self.id: 50 return True 51 # case 3 前驱节点标识符较大 52 elif self.id < self.predecessor.getId(): 53 if pointer > self.predecessor.getId() and pointer < ringLenth(m): 54 return True 55 if pointer < self.id: 56 return True 57 58 else: 59 return False 60 61 def getSucessor(self): 62 return self.sucessor 63 64 def getPredecessor(self): 65 return self.predecessor 66 67 def setSuceesor(self, node): 68 self.sucessor = node 69 70 def setPredecessor(self, node): 71 self.predecessor = node 72 73 def getId(self): 74 return self.id 75 76 77 def getFT(self): 78 return self.fingerTable 79 80 81 82 83 class DHT: 84 ''' 哈希表数据结构 ''' 85 def __init__(self, fileName, m): 86 self.nodeSet = {} 87 firstNode = None 88 for ip in open(fileName, 'r').readlines(): 89 node = Node(ip, m) 90 if firstNode is None: 91 firstNode = node 92 self.joinToRing(firstNode, node, m) 93 94 95 def joinToRing(self, curNode, wnode, m): 96 if len(self.nodeSet) is not 0: 97 sucessor = self.searchSucessor(curNode, wnode, m) 98 predecessor = sucessor.getPredecessor() 99 100 wnode.setPredecessor(predecessor) 101 wnode.setSuceesor(sucessor) 102 sucessor.setPredecessor(wnode) 103 predecessor.setSuceesor(wnode) 104 105 self.nodeSet[wnode.getId()] = wnode 106 self.updateNodesFT(m) 107 108 109 def searchSucessor(self, curNode, wnode, m): 110 if curNode.isInBound(wnode.getId(), m): 111 return curNode 112 else: 113 dis = (wnode.getId() - curNode.getId() + ringLenth(m)) % ringLenth(m) 114 # 寻找最大的 k 使得 2^k <= dis 115 maxk = 0 116 while((2 ** maxk) <= dis): maxk += 1 117 maxk -= 1 118 return self.searchSucessor(curNode.getFT()[2 ** maxk], wnode, m) 119 120 121 def updateNodesFT(self, m): 122 for node in self.nodeSet: 123 self.nodeSet[node].updateFT(m, self) 124 125 def getNodes(self): 126 return self.nodeSet.values() 127 128 129 130 131 class Test: 132 ''' 测试哈希表是否运行正常 ''' 133 def __init__(self, fileName, m): 134 self.dht = DHT(fileName, m) 135 for node in self.dht.getNodes(): 136 print("I am node %d, My predecessor is node %d, and My sucessor is node %d" 137 % (node.getId(), node.getPredecessor().getId(), node.getSucessor().getId())) 138 139 140 141 if __name__ == "__main__": 142 test = Test("DistributedHashTable\\IPAddressList.txt", 6)
|