class Peer_Remote(): # outbound connections def __init__(self, network_service, remote_ip, remote_port, context=None): self.exit = False self.network_service = network_service self.remote_ip = remote_ip self.remote_port = remote_port self.context = context Coro(self._server_connect) def _server_connect(self, coro=None): try: #logger.debug('CLIENT: connecting to peer at %s:%s', self.remote_ip, str(self.remote_port)) self.outbound_socket = AsynCoroSocket(socket.socket(socket.AF_INET, socket.SOCK_STREAM)) self.outbound_socket.setsockopt(socket.SOL_TCP, socket.TCP_NODELAY, 1) # if you're gonna act like UDP yield self.outbound_socket.connect((self.remote_ip, self.remote_port)) #logger.debug('CLIENT: connected to peer at %s:%s', self.remote_ip, str(self.remote_port)) self._send_coro = Coro(self._client_send) #Coro(self._client_recv) # unneeded if we don't utilize bi-directional communication in UDP style messaging self.network_service.on_server_connect(self, self.context) except: show_error() #raise def _client_recv(self, coro=None): while True: try: data = yield self.outbound_socket.recv_msg() if data == None or len(data) == 0 or self.exit: break #logger.debug('CLIENT: received data to peer at %s:%s (Data: %s)', self.remote_ip, str(self.remote_port), data) self.network_service.on_peer_data_received(data) except: show_error() #break #print "Coro(_client_recv) exiting" def _client_send(self, coro=None): coro.set_daemon() while True: try: cmd, state = yield self._send_coro.receive() data, context = state if cmd == NETWORK_PEER_DISCONNECT: self.network_service.on_client_disconnected(context) break #logger.debug('CLIENT: sending data to %s:%s (Data is: %s)', self.remote_ip, self.remote_port,data) yield self.outbound_socket.send_msg(data) self.network_service.on_client_data_sent(context) except: show_error() #break self.outbound_socket.shutdown(socket.SHUT_RDWR) self.outbound_socket.close() #logger.debug('CLIENT: disconnected from %s:%s', self.remote_ip, str(self.remote_port)) #print "Coro(_client_send) exiting" def send(self, data, context): if not self.exit: self._send_coro.send((None, (data, context))) def stop(self, context=None): self.exit = True #logger.debug('CLIENT: disconnecting from %s:%s', self.remote_ip, str(self.remote_port)) self._send_coro.send((NETWORK_PEER_DISCONNECT, (None,context)))
class Node_Service(Service): exit = False pause_scheduler = False def __init__(self, message_router): super(Node_Service, self).__init__(SERVICE_NODE, message_router, cl_name="ns") self.exit = False self.pause_scheduler = False self.nodes = {} self.queue = deque() self.delay_queue = deque() self.scheduler_thread = Thread(target=self._thread_scheduler) self.scheduler_thread.daemon = True self.scheduler_thread.start() self._stabilize_coro = Coro(self._coro_stabilize) def delay_enqueue(self,message,ms): if not self.pause_scheduler: self.delay_queue.append( (time.time(), ms, message)) def _thread_scheduler(self): try: while not self.exit: requeue = deque() while len(self.delay_queue) > 0 and not self.exit: queued_at, delay_ms, message = self.delay_queue.popleft() if (queued_at + delay_ms / 1000) < time.time(): #self.enqueue(message) self._stabilize_coro.send(message) else: requeue.append((queued_at, delay_ms, message)) while len(requeue) > 0 and not self.exit: self.delay_queue.append(requeue.popleft()) del requeue time.sleep(.1) # 10 ms except: show_error() print "Scheduler thread exiting" def _coro_stabilize(self, coro=None): coro.set_daemon() thread_pool = AsynCoroThreadPool(2 * multiprocessing.cpu_count()) while not self.exit: try: command, context = yield self._stabilize_coro.receive() if self.exit: # fast exit for now (non-graceful) break #command, context = self.queue.popleft() if self.pause_scheduler or context.join_on_stabilize: # just cycle the messages until unpaused if context.join_on_stabilize: context.send_message(Find_Successor_Message(context.thisNode, context.thisNode.key, context.thisNode), context.join_on_stabilize) context.join_on_stabilize = None delay = MAINTENANCE_PERIOD * 3 else: delay = MAINTENANCE_PERIOD self.delay_enqueue((command,context), delay) continue if command == "NODE_STABILIZE": yield thread_pool.async_task(coro, context.begin_stabilize) self.delay_enqueue( ("NODE_CHECK_PREDECESSOR", context), MAINTENANCE_PERIOD) elif command == "NODE_CHECK_PREDECESSOR": yield thread_pool.async_task(coro, context.begin_stabilize) yield thread_pool.async_task(coro, context.check_predecessor) self.delay_enqueue( ("NODE_FIX_FINGERS", context), MAINTENANCE_PERIOD ) elif command == "NODE_FIX_FINGERS": yield thread_pool.async_task(coro, context.fix_fingers, 10) self.delay_enqueue( ("NODE_STABILIZE", context), MAINTENANCE_PERIOD) except: show_error() print "Coro(stabilize) exiting" def stop(self, context=None): self.exit = True self._stabilize_coro.send((None,None)) #for i in range(2 * multiprocessing.cpu_count()): #self.queue.append(('terminate', context)) #self.signal_item_queued.set() def get_console_node(self): if len(self.nodes) > 0: return self.nodes[self.nodes.keys()[0]] def check_scheduler_pause(self,msg): if not self.pause_scheduler: return False if msg.type == Message_Recv_Peer_Data.Type(): msg = msg.network_msg result = False if msg.type == Stabilize_Reply_Message.Type(): result = True elif msg.type == Stablize_Message.Type(): result = True elif msg.type == Check_Predecessor_Message.Type(): result = True elif msg.type == Update_Message.Type(): result = True elif msg.type == Find_Successor_Message.Type(): result = True return result def handle_message(self, msg): if not msg.dest_service == self.service_id: raise Exception("Mismatched service recipient for message.") if msg.type == Message_Setup_Node.Type(): node = Node(self, msg.public_ip, msg.local_ip, msg.local_port) if msg.seeded_peers: for peer in msg.seeded_peers: node.join(peer) # use callback to try next peer if join fails (join is async) else: node.join() self.message_router.route( Message_Start_Server(node.local_ip, node.local_port, Message_Start_Server_Callback(self.service_id, node, True), Message_Start_Server_Callback(self.service_id, node, False))) elif msg.type == Message_Start_Server_Callback.Type(): if msg.result: self.nodes[str(msg.node)] = msg.node self.delay_enqueue(("NODE_STABILIZE", msg.node), 1000) else: msg.node.exit_network() raise Exception( "Unable to successfully start server for node at " + str(msg.node.thisNode)) if msg.type == Message_Forward.Type( ): ni = msg.origin_node if self.nodes.has_key(str(ni)): lnode = self.nodes[str(ni)] # get the Node class this message is addressed to (ip:port) forward_node = lnode.find_ideal_forward(msg.forward_hash) if msg.forward_msg.type == Database_Get_Message.Type() or msg.forward_msg.type == Database_Put_Message.Type(): msg.forward_msg.storage_node = forward_node if forward_node != ni: self.send_message(msg.forward_msg, forward_node) else: self.send_message(msg.forward_msg) elif msg.type == Message_Recv_Peer_Data.Type(): # came off the wire ni = Node_Info(msg.local_ip if len(msg.local_ip) > 0 else "127.0.0.1",msg.local_port) if self.nodes.has_key(str(ni)): lnode = self.nodes[str(ni)] # get the Node class this message is addressed to (ip:port) msg = msg.network_msg rnode = lnode.final_destination(msg) if rnode != lnode.thisNode: self.send_message(msg,rnode) return logger.debug(str(ni) + " received network msg: " + msg.type) if msg.type == Message_Forward.Type( ): lnode.find_ideal_forward(msg.forward_hash) #fix this...we need to do forward the message elif msg.type == Find_Successor_Message.Type(): self.send_message(Update_Message(lnode.thisNode, msg.reply_to.key, msg.finger), msg.reply_to) elif msg.type == Update_Message.Type( ): lnode.update_finger(msg.reply_to, msg.finger) elif msg.type == Check_Predecessor_Message.Type(): self.send_message(Update_Message(lnode.thisNode, msg.reply_to.key, 0), msg.reply_to) elif msg.type == Stablize_Message.Type( ): self.send_message(Stabilize_Reply_Message(lnode.thisNode, msg.reply_to.key, msg.reply_to)) elif msg.type == Stabilize_Reply_Message.Type(): lnode.stabilize(msg) elif msg.type == Notify_Message.Type(): lnode.get_notified(msg) elif msg.type == Exit_Message.Type(): lnode.peer_polite_exit(msg.reply_to) elif msg.type == Database_Put_Message.Type() or msg.type == Database_Get_Message.Type(): msg.storage_node = lnode.thisNode self.message_router.route(msg) elif msg.type == Database_Put_Message_Response.Type() or msg.type == Database_Get_Message_Response.Type(): self.message_router.route(msg) elif msg.type == Message_Console_Command.Type(): self.handle_command(msg.command,msg.args) # wraps message in network packet if not destined for local #def route_node_message(self,msg): # if self.nodes.has_key(str(msg.origin_node)): # node = self.nodes[str(msg.origin_node)] # forward_to = node.find_ideal_forward(msg.destination_key) # if forward_to == node: # self.send_message(msg) # else: # self.send_message(Message_Send_Peer_Data(forward_to, msg.serialize())) def handle_command(self,cmd,args=[]): if cmd == "print": for node in self.nodes.values(): print "successor ", node.thisNode.successor.print_key() print "predecessor", node.thisNode.predecessor.print_key() elif cmd == "pause": self.pause_scheduler = True elif cmd == "resume": self.pause_scheduler = False