private Raft.IServer thriftServer; //INTERFACE IMPLEMENTARDA PELO SERVIDOR THRIFT, ATRAVEZ DESSA INTERFACE É POSSIVEL REALMENTE EXECUTAR AS FUNÇÕES THRIFT public Node(List <Tuple <string, List <int> > > nodes, int node_index, Raft.IServer thriftServer) { this.t = 0; this.ip = nodes[node_index].Item1; this.port = nodes[node_index].Item2; this.current_state = State.Follower; this.nodes = new Dictionary <Tuple <string, int>, TCP.TCPConnection>(); this.nodes_acks = new Dictionary <Tuple <string, int>, int>(); this.cluster_id = ""; Tuple <int, int>[,] topology = this.PortTopology(nodes); for (int i = 0; i < nodes.Count(); i++) { if (i == node_index) { continue; } var IPLocal = new Tuple <string, int>(this.ip, topology[node_index, i].Item1); var IPRemote = new Tuple <string, int>(nodes[i].Item1, topology[node_index, i].Item2); var connection = new TCP.TCPConnection(IPLocal, IPRemote); this.nodes.Add(IPRemote, connection); this.nodes_acks.Add(IPRemote, 0); //TODOS NODOS COMEÇAM SEM NENHUM COMMIT REALIZADO } this.election_time = new System.Timers.Timer(Random.random.Next(1000, 2000)); this.heartbeat_time = new System.Timers.Timer((int)(this.election_time.Interval / 3)); this.election_time.Stop(); this.heartbeat_time.Stop(); this.election_time.AutoReset = true; this.heartbeat_time.AutoReset = true; this.election_time.Elapsed += Election; this.heartbeat_time.Elapsed += HeartBeat; this.log = new Log(); this.thriftServer = thriftServer; Console.WriteLine("RAFT :: Create node with election_time:{0}ms", this.election_time.Interval); }
private void Listen(TCP.TCPConnection connection) { for (string buffer = null; true; buffer = connection.ReceiveMessage()) { if (buffer == null || buffer == string.Empty) { continue; } foreach (var message in buffer.Split('\0').Where(str => str != string.Empty)) { if (message.Split('|').Length != 6 || !message.Split('|')[3].Trim().Equals("null")) { Console.WriteLine("RAFT :: RECEIVE FROM <{0}:{1}> AT TERM={2} :: {3}", connection.IPRemote.Address, connection.IPRemote.Port, this.t, message.Trim()); } if (message.Contains("request vote for me")) { if (int.Parse(message.Split('|')[0]) > this.t) { this.t = int.Parse(message.Split('|')[0]); if (this.log.last_committed_index <= (int.Parse(message.Split('|')[2]))) //APENAS VOTA SIM SE A ULTIMA ENTRADA DO CANTIDO FOR MAIOR QUE A SUA, OU SEJA ESTA MAIS ATUALIZADO { connection.SendMessage(string.Format("{0}|vote yes", t)); } else { connection.SendMessage(string.Format("{0}|vote no", t)); } } else { connection.SendMessage(string.Format("{0}|vote no", t)); } } else if (message.Contains("vote yes")) { if (int.Parse(message.Split('|')[0]) == this.t) { this.nVotes++; } } else if (message.Contains("win")) { this.election_time.Stop(); var t = int.Parse(message.Split('|')[3]); if (this.t <= t) { this.current_state = State.Follower; this.heartbeat_time.Stop(); this.election_time.Start(); this.t = t; } //this.thriftServer.Commit(string.Format("FollowUpdateLeader({0}, {1});", this.cluster_id, this.cluster_id = message.Split('|')[5])); } else if (message.Contains("AE")) { this.resetTimer(); var t = int.Parse(message.Split('|')[0]); if (this.t <= t) { this.current_state = State.Follower; this.heartbeat_time.Stop(); this.t = t; this.cluster_id = message.Split('|')[5]; } var entryId = int.Parse(message.Split('|')[2]); var LastEntry = this.log.LastEntry(); if (LastEntry == null) { LastEntry = new Entry() { id = -1 }; } if (LastEntry.id <= entryId) //CHEGOU UMA ENTRADA ORDENADA, COMITA A ULTIMA E CONCATENA A NOVA { this.log.Commit(this.thriftServer); foreach (var server in this.nodes.Keys) //TODO SEGUIDOR ASSUME QUE SE ELE RECEBEU TODA A REDE TAMBÉM RECEBEU, CASO NÃO SEJA VERDADE //QUANDO ESSE SEGUIDOR ASSUMIR A LIDERANÇA, UM OUTRO SEGUIDOR IRÁ SOLICITAR O REGISTRO QUE É NECESSARIO { this.nodes_acks[server] = this.log.last_committed_index + 1; } if (LastEntry.id < entryId - 1) //CHEGOU UMA ENTRADA MUITO A FRENTE { connection.SendMessage(string.Format("NACK|{0}|{1}", this.t, LastEntry.id + 1)); //SOLICITA A ENTRADA DA SEQUENCIA continue; //DESCARTA A ENTRADA RECEBIDA (MANTEM O LOG ORDENADO) } if (message.Split('|')[3].Equals("null")) //APENAS SINAL DE HEARTBEAT NÃO É UM ENVIO DE LOG { continue; } LastEntry = new Entry() { id = entryId, message = message.Split('|')[3], }; this.log.append(LastEntry); connection.SendMessage(string.Format("ACK|{0}|{1}", this.t, LastEntry.id)); //ACEITA A NOVA MENSAGEM } } else if (message.Contains("NACK")) { var requestedId = int.Parse(message.Split('|')[2]); var tuple = Tuple.Create <string, int>(connection.IPRemote.Address.ToString(), connection.IPRemote.Port); this.nodes_acks[tuple] = requestedId; } else if (message.Contains("ACK")) { var id = int.Parse(message.Split('|')[2]); var tuple = Tuple.Create <string, int>(connection.IPRemote.Address.ToString(), connection.IPRemote.Port); this.log.SetAck(id, tuple, this.nodes.Count()); this.nodes_acks[tuple] = id + 1; //Console.WriteLine("NEXT FOR ME " + tuple.Item1 + ":" + tuple.Item2 + ">>>" + this.nodes_acks[tuple]); } } } }