Skip to content

Commit ac6a931

Browse files
committed
Code and screenshots added.
1 parent aa6ef04 commit ac6a931

7 files changed

+208
-1
lines changed

README.md

+44-1
Original file line numberDiff line numberDiff line change
@@ -1 +1,44 @@
1-
# Golang-BullyAlgorithm
1+
# Golang-BullyAlgorithm
2+
3+
### Introduction
4+
This project focuses on skeleton implemention of the Bully Algorithm in Leader Election methodology in Distributed Systems using Remote Procedure Calls(RPC) in Go Language.
5+
6+
### Implementation
7+
Major implementation contraints or strategies involve-
8+
* Each node is aware of the other's node network information, i.e. IP address and port
9+
* There is one file which needs to be built and the program needs to run on several terminal windows, each representing a node in the network. IP addresses is 127.0.0.1 and port numbers range from 3000-3004.
10+
* For ease of implementation, 5 nodes are allowed by default. More nodes can be added with minimal changes in the code.
11+
* By default, 5th node is the coordinator.
12+
* To invoke elections, close the program of coordinator using *ctrl + c* and try communicating to the coordinator from other nodes.
13+
* Upon the recovery of the old coordinator, elections can be invoked to elect the new coordinator again.
14+
15+
### Output interface
16+
Output interface contains 3 major components-
17+
1. Selecting id of the node
18+
1. Give an option to state if the node just recovered from a crash
19+
1. Communicate with the coordinator
20+
21+
![Output interface] (/screenshots/output_interface.png)
22+
23+
### RPC methods:
24+
* **Election**
25+
Handles election received from the nodes with lower id.by sending them OK message along with the node invoking election. Checks like `no_election_invoked: bool` are present to prevent multiple elections by a single host.
26+
27+
* **NewCoordinator**
28+
This function is used by the coodinator by calling this function as broadcast in other nodes to update the coordinator id as the last stage of the Bully Algorithm.
29+
30+
* **HandleCommunication**
31+
This function is called by the particpants to communicate to the coordinator and get response from the function. Fail in calling or getting response this function from coordinator triggers the election.
32+
33+
### Screenshots
34+
_Fig: When coordinator node 5 is active and the other nodes are able to communicate with the coordinator_
35+
![Coordinator active] (/screenshots/coordinator_active.png)
36+
37+
_Fig: When coordinator node is closed, Elections! 4 is the new coordinator after the elections._
38+
![Election] (/screenshots/elections.png)
39+
40+
_Fig: Post election communication. 4 is the new coordinator and 3 is able to communicate with the new coordinator._
41+
![Post elections] (/screenshots/post_election.png)
42+
43+
_Fig: Recovering from crash. 5 is coordinator again and 1 is able to communicate with new coordinator._
44+
![Recovery elections] (/screenshots/recovery_elections.png)

bullyAlgorithm.go

+164
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
2+
package main
3+
4+
import (
5+
"fmt"
6+
"net"
7+
"net/rpc"
8+
"log"
9+
)
10+
11+
// Reply for getting response like OK messages from the RPC calls.
12+
type Reply struct{
13+
Data string
14+
}
15+
16+
// Core Bully Algorithm structure. It contains functions registered for RPC.
17+
// it also contains information regarding other sites
18+
type BullyAlgorithm struct{
19+
my_id int
20+
coordinator_id int
21+
ids_ip map[int]string
22+
}
23+
24+
// if a site has already invoked election, it doesnt need to start elections again
25+
var no_election_invoked = true
26+
27+
// This is the election function which is invoked when a smaller host id requests for an election to this host
28+
func (bully *BullyAlgorithm) Election(invoker_id int, reply *Reply) error{
29+
fmt.Println("Log: Receiving election from", invoker_id)
30+
if invoker_id < bully.my_id{
31+
fmt.Println("Log: Sending OK to", invoker_id)
32+
reply.Data = "OK" // sends OK message to the small site
33+
if no_election_invoked{
34+
no_election_invoked = false
35+
go invokeElection() // invokes election to its higher hosts
36+
}
37+
}
38+
return nil
39+
}
40+
41+
var superiorNodeAvailable = false // Toggled when any superior host sends OK message
42+
43+
// This function invokes the election to its higher hosts. It sends its Id as the parameter while calling the RPC
44+
func invokeElection(){
45+
for id,ip := range bully.ids_ip{
46+
reply := Reply{""}
47+
if id > bully.my_id{
48+
fmt.Println("Log: Sending election to", id)
49+
client, error := rpc.Dial("tcp",ip)
50+
if error != nil{
51+
fmt.Println("Log:", id, "is not available.")
52+
continue
53+
}
54+
err := client.Call("BullyAlgorithm.Election", bully.my_id, &reply)
55+
if err != nil{
56+
fmt.Println(err)
57+
fmt.Println("Log: Error calling function", id, "election")
58+
continue
59+
}
60+
if reply.Data == "OK"{ // Means superior host exists
61+
fmt.Println("Log: Received OK from", id)
62+
superiorNodeAvailable = true
63+
}
64+
}
65+
}
66+
if !superiorNodeAvailable{ // if no superior site is active, then the host can make itself the coordinator
67+
makeYourselfCoordinator()
68+
}
69+
superiorNodeAvailable = false
70+
no_election_invoked = true // reset the election invoked
71+
}
72+
73+
// This function is called by the new Coordinator to update the coordinator information of the other hosts
74+
func (bully *BullyAlgorithm) NewCoordinator(new_id int, reply *Reply) error{
75+
bully.coordinator_id = new_id
76+
fmt.Println("Log:", bully.coordinator_id, "is now the new coordinator")
77+
return nil
78+
}
79+
80+
func (bully *BullyAlgorithm) HandleCommunication(req_id int, reply *Reply) error{
81+
fmt.Println("Log: Receiving communication from", req_id)
82+
reply.Data = "OK"
83+
return nil
84+
}
85+
86+
func communicateToCoordinator(){
87+
coord_id := bully.coordinator_id
88+
coord_ip := bully.ids_ip[coord_id]
89+
fmt.Println("Log: Communicating coordinator", coord_id)
90+
my_id := bully.my_id
91+
reply := Reply{""}
92+
client, err := rpc.Dial("tcp", coord_ip)
93+
if err != nil{
94+
fmt.Println("Log: Coordinator",coord_id, "communication failed!")
95+
fmt.Println("Log: Invoking elections")
96+
invokeElection()
97+
return
98+
}
99+
err = client.Call("BullyAlgorithm.HandleCommunication", my_id, &reply)
100+
if err != nil || reply.Data != "OK"{
101+
fmt.Println("Log: Communicating coordinator", coord_id, "failed!")
102+
fmt.Println("Log: Invoking elections")
103+
invokeElection()
104+
return
105+
}
106+
fmt.Println("Log: Communication received from coordinator", coord_id)
107+
}
108+
109+
// This function is called when the host decides that it is the coordinator.
110+
// it broadcasts the message to all other hosts and updates the leader info, including its own host.
111+
func makeYourselfCoordinator(){
112+
reply := Reply{""}
113+
for id, ip := range bully.ids_ip{
114+
client, error := rpc.Dial("tcp", ip)
115+
if error != nil{
116+
fmt.Println("Log:", id, "communication error")
117+
continue
118+
}
119+
client.Call("BullyAlgorithm.NewCoordinator", bully.my_id, &reply)
120+
}
121+
}
122+
123+
// Core object of bully algorithm initialized with all ip addresses of all other sites in the network
124+
var bully = BullyAlgorithm{
125+
my_id: 1,
126+
coordinator_id: 5,
127+
ids_ip: map[int]string{ 1:"127.0.0.1:3000", 2:"127.0.0.1:3001", 3:"127.0.0.1:3002", 4:"127.0.0.1:3003", 5:"127.0.0.1:3004"}}
128+
129+
130+
func main(){
131+
my_id := 0
132+
fmt.Printf("Enter the site id[1-5]: ") // initialize the host id at the run time
133+
fmt.Scanf("%d", &my_id)
134+
bully.my_id = my_id
135+
my_ip := bully.ids_ip[bully.my_id]
136+
address, err := net.ResolveTCPAddr("tcp", my_ip)
137+
if err != nil{
138+
log.Fatal(err)
139+
}
140+
inbound, err := net.ListenTCP("tcp", address)
141+
if err != nil{
142+
log.Fatal(err)
143+
}
144+
rpc.Register(&bully)
145+
fmt.Println("server is running with IP address and port number:", address)
146+
go rpc.Accept(inbound) // Accepting connections from other hosts.
147+
148+
reply := ""
149+
fmt.Printf("Is this node recovering from a crash?(y/n): ") // Recovery from crash.
150+
fmt.Scanf("%s", &reply)
151+
if reply == "y"{
152+
fmt.Println("Log: Invoking Elections")
153+
invokeElection()
154+
}
155+
156+
random := ""
157+
for{
158+
fmt.Printf("Press enter for %d to communicate with coordinator.\n", bully.my_id)
159+
fmt.Scanf("%s", &random)
160+
communicateToCoordinator()
161+
fmt.Println("")
162+
}
163+
fmt.Scanf("%s", &random)
164+
}

screenshots/coordinator_active.png

201 KB
Loading

screenshots/elections.png

251 KB
Loading

screenshots/output_interface.png

26.6 KB
Loading

screenshots/post_election.png

260 KB
Loading

screenshots/recovery_elections.png

297 KB
Loading

0 commit comments

Comments
 (0)