Load Balancer Simulator
CSCE 412 - Load Balancer Simulation Project
LoadBalancer.cpp
Go to the documentation of this file.
1 
6 #include "LoadBalancer.h"
7 #include <iostream>
8 #include <sstream>
9 
10 // COLORS :P
11 const std::string RED = "\033[31m";
12 const std::string GREEN = "\033[32m";
13 const std::string YELLOW = "\033[33m";
14 const std::string BLUE = "\033[34m";
15 const std::string MAGENTA = "\033[35m";
16 const std::string CYAN = "\033[36m";
17 const std::string RESET = "\033[0m";
18 
19 LoadBalancer::LoadBalancer(int numServers, int maxTime, int queueMin, int queueMax,
20  int scalingCooldown, double newRequestProb,
21  int minRequestTime, int maxRequestTime,
22  const std::string& logFileName) {
23 
24  this->currTime = 0;
25  this->maxTime = maxTime;
26  this->cooldownRemaining = 0;
27  this->nextServerId = 0;
28  this->queueMin = queueMin;
29  this->queueMax = queueMax;
30  this->scalingCooldown = scalingCooldown;
31  this->generateRequestProbability = newRequestProb;
32  this->minRequestTime = minRequestTime;
33  this->maxRequestTime = maxRequestTime;
34  this->totalProcessedRequests = 0;
35  this->totalRejectedRequests = 0;
36  this->serversAdded = 0;
37  this->serversRemoved = 0;
38  this->startingQueueSize = 0;
39 
40  logFile.open(logFileName);
41 
42  // create servers
43  for(int i = 0; i < numServers; i++) {
44  WebServer* server = new WebServer(nextServerId++);
45  servers.push_back(server);
46  availableQueue.push(server);
47  }
48 
49  log("LoadBalancer created with " + std::to_string(numServers) + " servers", CYAN);
50 
51 }
52 
54  for (int i = 0; i < servers.size(); i++) delete servers[i];
55  servers.clear();
56 
57  if (logFile.is_open()) logFile.close();
58 }
59 
60 // getter functions
61 int LoadBalancer::getQueueSize() const { return requestQueue.size(); }
62 int LoadBalancer::getServerCount() const { return servers.size(); }
63 
64 // FIREWALL
65 void LoadBalancer::addBlockedIP(const std::string& startIP, const std::string& stopIP) {
66  firewallRange.push_back(std::make_pair(startIP, stopIP));
67  log("Blocked IP range: " + startIP + " - " + stopIP, RED);
68 }
69 
70 unsigned long LoadBalancer::ipToLong(const std::string& ip) {
71  unsigned long result = 0;
72  unsigned long octet = 0;
73  int shift = 24;
74 
75  for (int i = 0; i < ip.length(); i++) {
76  if (ip[i] == '.') {
77  result |= (octet << shift);
78  shift -= 8;
79  octet = 0;
80  } else {
81  octet = octet * 10 + (ip[i] - '0');
82  }
83  }
84  result |= (octet << shift);
85 
86  return result;
87 }
88 
89 bool LoadBalancer::isBlockedIP(const std::string& ip) const {
90  unsigned long ipNum = ipToLong(ip);
91 
92  for (int i = 0; i < firewallRange.size(); i++) {
93  unsigned long start = ipToLong(firewallRange[i].first);
94  unsigned long end = ipToLong(firewallRange[i].second);
95  if (ipNum >= start && ipNum <= end) {
96  return true;
97  }
98  }
99  // found no blocks, so it works
100  return false;
101 }
102 
103 // Queue
104 
106  int count = servers.size() * 100;
107 
108  for (int i = 0; i < count; i++) {
110 
111  if (!isBlockedIP(req.ipIn)) {
112  requestQueue.push(req);
113  log("Request #" + std::to_string(i) + ": Added (" + req.ipIn + ")", CYAN);
114  } else { // reject and dont put in
116  log("Request #" + std::to_string(i) + ": REJECTED because of bad IP (" + req.ipIn + ")", RED);
117  }
118  }
119 
121  log("Queue initialized with " + std::to_string(startingQueueSize) + " requests", CYAN);
122 }
123 
124 
125 // methods in cycle
126 
128  double roll = (double)rand() / RAND_MAX;
129 
130  if (roll < generateRequestProbability) {
132 
133  if (isBlockedIP(req.ipIn)) {
135  log("[Cycle " + std::to_string(currTime) + "] BLOCKED request from " + req.ipIn, RED);
136  } else {
137  requestQueue.push(req);
138  }
139  }
140 }
141 
142 // go through each webserver and tick, if a webserver finishes a request, it becomes available again
144  for (int i = 0; i < servers.size(); i++) {
145  if (servers[i]->tick()) { // become available
147  availableQueue.push(servers[i]);
148  log("[Cycle " + std::to_string(currTime) + "] Server " +
149  std::to_string(servers[i]->getId()) + " finished a request", GREEN);
150  }
151  }
152 }
153 
154 
155 // FIFO queue, assigns a server to the request until servers all full, or requests empty
157  while (!availableQueue.empty() && !requestQueue.empty()) {
158  WebServer* server = availableQueue.front();
159  availableQueue.pop();
160 
161  Request req = requestQueue.front();
162  requestQueue.pop();
163 
164  server->assignRequest(new Request(req));
165  log("[Cycle " + std::to_string(currTime) + "] Assigned request to Server " +
166  std::to_string(server->getId()) + " (time: " + std::to_string(req.time) +
167  ", type: " + req.jobType + ")", BLUE);
168  }
169 }
170 
171 // if queu size goes above what it should be , add a server, wait n clock cycles
173  // tick down
174  if (cooldownRemaining > 0) {
176  return;
177  }
178 
179  int queueSize = requestQueue.size();
180  int numServers = servers.size();
181 
182  // high traffic - add a server
183  if (queueSize > queueMax * numServers) {
184  WebServer* server = new WebServer(nextServerId++);
185  servers.push_back(server);
186  availableQueue.push(server);
187  serversAdded++;
189 
190  log("[Cycle " + std::to_string(currTime) + "] SCALED UP: added Server " +
191  std::to_string(server->getId()) + " (queue: " + std::to_string(queueSize) +
192  ", servers: " + std::to_string(servers.size()) + ")", YELLOW);
193  }
194  // low traffic - remove UNUSED server
195  else if (queueSize < queueMin * numServers && numServers > 1) {
196  // Only remove a server that is idle (in the available queue)
197  if (availableQueue.empty()) {
198  return; // all servers busy, can't remove any
199  }
200 
201  // Pop an idle server from the available queue
202  WebServer* server = availableQueue.front();
203  availableQueue.pop();
204 
205  // Remove it from the servers vector
206  for (int i = 0; i < servers.size(); i++) {
207  if (servers[i] == server) {
208  servers.erase(servers.begin() + i);
209  break;
210  }
211  }
212 
213  int removedId = server->getId();
214  delete server;
215  serversRemoved++;
217 
218  log("[Cycle " + std::to_string(currTime) + "] SCALED DOWN: removed Server " +
219  std::to_string(removedId) + " (queue: " + std::to_string(queueSize) +
220  ", servers: " + std::to_string(servers.size()) + ")", MAGENTA);
221  }
222 }
223 
224 // log to logfile and print in terminal message
225 void LoadBalancer::log(const std::string& message, const std::string& color) {
226  if (logFile.is_open()) {
227  logFile << message << std::endl;
228  }
229 
230  if (color.empty()) {
231  std::cout << message << std::endl;
232  } else {
233  std::cout << color << message << RESET << std::endl;
234  }
235 }
236 
237 // main simulation run
239  log("=== Simulation Starting ===", CYAN);
240  log("Servers: " + std::to_string(servers.size()), CYAN);
241  log("Max time: " + std::to_string(maxTime) + " cycles", CYAN);
242  log("Queue size: " + std::to_string(requestQueue.size()), CYAN);
243  log("Task time range: " + std::to_string(minRequestTime) + " - " + std::to_string(maxRequestTime), CYAN);
244  log("");
245  log("");
246  log("");
247 
248  log("=== Simulation Running ===", CYAN);
249 
250  for (currTime = 0; currTime < maxTime; currTime++) {
251  generateNewRequest(); // create new request (% chance of occurance)
252  tickAllServers(); // tick servers and change availability
253  assignRequest(); // assign requests in queue to servers
254  checkScaling(); // if overclocked, add more servers, do reverse as well
255  }
256 
257  log("");
258  log("");
259  log("");
260  log("=== Simulation Complete ===", CYAN);
261 
262 }
263 
265  int idleCount = availableQueue.size();
266  int busyCount = servers.size() - idleCount;
267 
268  log("");
269  log("=== Summary & End of Simulation STATS ===", CYAN);
270  log("Starting queue size: " + std::to_string(startingQueueSize));
271  log("Ending queue size: " + std::to_string(requestQueue.size()));
272  log("Total processed: " + std::to_string(totalProcessedRequests));
273  log("Total rejected: " + std::to_string(totalRejectedRequests));
274  log("Task time range: " + std::to_string(minRequestTime) + " - " + std::to_string(maxRequestTime));
275  log("Servers added: " + std::to_string(serversAdded));
276  log("Servers removed: " + std::to_string(serversRemoved));
277  log("Final server count: " + std::to_string(servers.size()));
278  log("Busy servers: " + std::to_string(busyCount));
279  log("Idle servers: " + std::to_string(idleCount));
280  log("=================================================");
281 }
const std::string RED
const std::string RESET
const std::string YELLOW
const std::string BLUE
const std::string MAGENTA
const std::string CYAN
const std::string GREEN
Defines the LoadBalancer class that manages servers and the request queue.
std::vector< WebServer * > servers
All active server instances.
Definition: LoadBalancer.h:30
double generateRequestProbability
Probability of generating a new request each cycle.
Definition: LoadBalancer.h:42
int serversRemoved
Number of servers removed during simulation.
Definition: LoadBalancer.h:50
static unsigned long ipToLong(const std::string &ip)
Converts a dotted-decimal IP address string to an unsigned long.
int nextServerId
ID to assign to the next created server.
Definition: LoadBalancer.h:36
std::ofstream logFile
Output stream for the log file.
Definition: LoadBalancer.h:53
std::queue< WebServer * > availableQueue
Queue of idle servers ready for assignment.
Definition: LoadBalancer.h:29
int queueMax
Upper queue threshold per server for scaling up.
Definition: LoadBalancer.h:40
void run()
Runs the load balancer simulation for the configured number of clock cycles.
std::vector< std::pair< std::string, std::string > > firewallRange
Blocked IP ranges (start, end)
Definition: LoadBalancer.h:31
int maxTime
Total number of clock cycles to simulate.
Definition: LoadBalancer.h:34
~LoadBalancer()
Destructor. Frees all server instances and closes the log file.
int queueMin
Lower queue threshold per server for scaling down.
Definition: LoadBalancer.h:39
int cooldownRemaining
Clock cycles remaining before next scaling action.
Definition: LoadBalancer.h:35
int serversAdded
Number of servers added during simulation.
Definition: LoadBalancer.h:49
int totalProcessedRequests
Total requests successfully processed.
Definition: LoadBalancer.h:47
LoadBalancer(int numServers, int maxTime, int queueMin, int queueMax, int scalingCooldown, double newRequestProb, int minRequestTime, int maxRequestTime, const std::string &logFileName)
Constructs a LoadBalancer with the given configuration.
void addBlockedIP(const std::string &startIP, const std::string &stopIP)
Adds a blocked IP address range to the firewall.
int getServerCount() const
Returns the current number of active servers.
int startingQueueSize
Queue size after initial population.
Definition: LoadBalancer.h:51
int maxRequestTime
Maximum processing time for generated requests.
Definition: LoadBalancer.h:44
void initializeQueue()
Populates the request queue with an initial batch of requests (servers * 100).
bool isBlockedIP(const std::string &ip) const
Checks if an IP address falls within any blocked firewall range.
int currTime
Current simulation clock cycle.
Definition: LoadBalancer.h:33
int totalRejectedRequests
Total requests rejected by the firewall.
Definition: LoadBalancer.h:48
int minRequestTime
Minimum processing time for generated requests.
Definition: LoadBalancer.h:43
void log(const std::string &message, const std::string &color="")
Logs a message to the log file and prints it to the console.
void tickAllServers()
Advances all servers by one clock cycle and marks finished servers as available.
int getQueueSize() const
Returns the current number of pending requests in the queue.
int scalingCooldown
Cooldown period (cycles) between scaling actions.
Definition: LoadBalancer.h:41
void printSummary()
Prints a summary of simulation statistics to the log and console.
void generateNewRequest()
Randomly generates a new request and adds it to the queue (based on probability).
void assignRequest()
Assigns pending requests from the queue to available servers.
std::queue< Request > requestQueue
FIFO queue of pending requests.
Definition: LoadBalancer.h:28
void checkScaling()
Checks queue thresholds and adds or removes servers as needed.
Simulates a web server that receives and processes requests from the load balancer.
Definition: WebServer.h:17
void assignRequest(Request *req)
Assigns a new request to this server for processing.
Definition: WebServer.cpp:42
int getId()
Returns the server's unique ID.
Definition: WebServer.cpp:22
Represents a single network request with source/destination IPs, processing time, and job type.
Definition: Request.h:14
int time
Processing time in clock cycles.
Definition: Request.h:17
static Request generateRandom(int minTime, int maxTime)
Generates a random Request with random IPs and a random processing time.
Definition: Request.cpp:23
std::string ipIn
Source IP address of the request.
Definition: Request.h:15
char jobType
Job type: 'P' for processing, 'S' for streaming.
Definition: Request.h:18