You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

143 lines
5.3 KiB

  1. from argparse import ArgumentParser
  2. from bisect import bisect_left, bisect_right
  3. from threading import Thread
  4. from xmlrpc.client import ServerProxy
  5. from xmlrpc.server import SimpleXMLRPCServer
  6. import traceback
  7. M = 5
  8. PORT = 1234
  9. RING = [2, 7, 11, 17, 22, 27]
  10. class Node:
  11. def __init__(self, node_id):
  12. """Initializes the node properties and constructs the finger table according to the Chord formula"""
  13. # Assuming that the program knows all the nodes and stores them in a sorted array RING
  14. self.node_id = node_id
  15. self.finger_table = []
  16. self.successor_id = RING[(RING.index(node_id) + 1) % len(RING)]
  17. self.predecessor_id = RING[RING.index(node_id) - 1]
  18. self.table = {}
  19. for i in range(M):
  20. self.finger_table.append(RING[bisect_left(RING, ((node_id + (2 ** i)) % (2 ** M))) % len(RING)])
  21. print(f"Node created! Finger table = {self.finger_table}, [pred, succ] = [{self.predecessor_id}, {self.successor_id}]")
  22. def closest_preceding_node(self, id):
  23. """Returns node_id of the closest preceeding node (from n.finger_table) for a given id"""
  24. for i in reversed(self.finger_table):
  25. if i == RING[-1]:
  26. idx = bisect_left([RING[0], RING[-1]], id)
  27. if idx == 0 or idx == 2:
  28. return i
  29. elif self.node_id > i:
  30. idx = bisect_left([i, self.node_id], id)
  31. if idx == 1:
  32. return i
  33. else:
  34. if i > self.node_id and i < id:
  35. return i
  36. return self.finger_table[-1]
  37. def find_successor(self, id):
  38. """Recursive function returning the identifier of the node responsible for a given id"""
  39. if id == self.node_id:
  40. return id
  41. # Note the half-open interval and that L <= R does not necessarily hold
  42. if self.successor_id < self.node_id:
  43. idx = bisect_left([self.successor_id, self.node_id], id)
  44. if idx == 0 or idx == 2:
  45. return self.successor_id
  46. elif id in range(self.node_id, self.successor_id + 1):
  47. return self.successor_id
  48. # Forward the query to the closest preceding node in the finger table for n
  49. n0 = self.closest_preceding_node(id)
  50. print(f'Forwarding request to node {n0}')
  51. with ServerProxy(f'http://node_{n0}:{PORT}') as proxy:
  52. return proxy.find_successor(id)
  53. def put(self, key, value):
  54. """Stores the given key-value pair in the node responsible for it"""
  55. try:
  56. print(f"put({key}, {value})")
  57. if self.node_id < self.predecessor_id:
  58. idx = bisect_left([self.node_id, self.predecessor_id], key)
  59. if idx == 0 or idx == 2:
  60. return self.store_item(key, value)
  61. elif key in range(self.predecessor_id, self.node_id + 1):
  62. return self.store_item(key, value)
  63. n0 = self.find_successor(key)
  64. if self.node_id == n0:
  65. return self.store_item(key, value)
  66. with ServerProxy(f'http://node_{n0}:{PORT}') as proxy:
  67. return proxy.store_item(key, value)
  68. except Exception as e:
  69. print(f"couldn't put({key}, {value})")
  70. print(traceback.format_exc())
  71. print(e)
  72. return False
  73. def get(self, key):
  74. """Gets the value for a given key from the node responsible for it"""
  75. try:
  76. print(f"get({key})")
  77. if self.node_id < self.predecessor_id:
  78. idx = bisect_left([self.node_id, self.predecessor_id], key)
  79. if idx == 0 or idx == 2:
  80. return self.retrieve_item(key)
  81. elif key in range(self.predecessor_id, self.node_id + 1):
  82. return self.retrieve_item(key)
  83. n0 = self.find_successor(key)
  84. if self.node_id == n0:
  85. return self.retrieve_item(key)
  86. with ServerProxy(f'http://node_{n0}:{PORT}') as proxy:
  87. return proxy.retrieve_item(key)
  88. except Exception as e:
  89. print(f"couldn't get({key})")
  90. print(traceback.format_exc())
  91. print(e)
  92. return -1
  93. def store_item(self, key, value):
  94. """Stores a key-value pair into the data store of this node"""
  95. self.table[key] = value
  96. return True
  97. def retrieve_item(self, key):
  98. """Retrieves a value for a given key from the data store of this node"""
  99. if key in self.table:
  100. return self.table[key]
  101. return -1
  102. if __name__ == '__main__':
  103. try:
  104. parser = ArgumentParser()
  105. parser.add_argument('node_id')
  106. args = parser.parse_args()
  107. node = Node(int(args.node_id))
  108. server = SimpleXMLRPCServer(('0.0.0.0', PORT))
  109. print("Listening on port 1234...")
  110. server.register_function(node.get, "get")
  111. server.register_function(node.put, "put")
  112. server.register_function(node.retrieve_item, "retrieve_item")
  113. server.register_function(node.store_item, "store_item")
  114. server.register_function(node.find_successor, "find_successor")
  115. server.serve_forever()
  116. except KeyboardInterrupt:
  117. print("node killed...")
  118. exit()