Load Balancer Simulation
A simulation of a load balancer with dynamic server scaling in C++
Loading...
Searching...
No Matches
LoadBalancer.cpp
Go to the documentation of this file.
1
5
6#include "LoadBalancer.h"
7#include "Logger.h"
8#include <iostream>
9#include <sstream>
10#include <iomanip>
11#include <algorithm>
12
14const char PROCESS_JOB = 'P';
16const char STREAMING_JOB = 'S';
17
18namespace {
19 const char* RESET = "\033[0m";
20 const char* CYAN = "\033[36m";
21 const char* GREEN = "\033[32m";
22 const char* YELLOW = "\033[33m";
23 const char* MAGENTA = "\033[35m";
24 const char* BOLD = "\033[1m";
25 const char* RED = "\033[31m";
26}
27
28void LoadBalancer::log(const char* colorCode, const char* msg) const {
29 std::ostringstream oss;
30 if (config_.colorsEnabled) {
31 oss << CYAN << "[" << RESET << BOLD << currentTime_ << RESET << CYAN << "]" << RESET
32 << " " << colorCode << msg << RESET << "\n";
33 } else {
34 oss << "[" << currentTime_ << "] " << msg << "\n";
35 }
36 Logger::log(oss.str());
37}
38
39
40LoadBalancer::LoadBalancer(int initialServerCount, const LoadBalancerConfig& config)
41 : config_(config),
42 currentTime_(0),
43 minQueueThreshold_(config.minThreshold),
44 maxQueueThreshold_(config.maxThreshold),
47 rng_(std::random_device()()),
48 requestDist_(config.requestProbability),
49 timeDist_(config.minProcessingTime, config.maxProcessingTime),
50 octetDist_(0, 255),
51 jobTypeDist_(0, 1),
57 {
58 for (int i = 0; i < initialServerCount; ++i) {
59 servers_.push_back(std::make_unique<WebServer>(nextServerId_++));
60 }
61 minQueueThreshold_ = config.minThreshold * static_cast<int>(servers_.size());
62 maxQueueThreshold_ = config.maxThreshold * static_cast<int>(servers_.size());
63}
64
66 std::ostringstream oss;
67 oss << octetDist_(rng_) << "." << octetDist_(rng_) << "."
68 << octetDist_(rng_) << "." << octetDist_(rng_);
69 return oss.str();
70}
71
73 static const char jobTypes[] = {PROCESS_JOB, STREAMING_JOB};
74 Request request;
75 request.ipIn = generateRandomIP();
76 request.ipOut = generateRandomIP();
77 request.jobType = jobTypes[jobTypeDist_(rng_)];
78 request.timeRequired = timeDist_(rng_);
79 return request;
80}
81
83 for (int i = 0; i < count; ++i) {
85 }
86 if (count > 0) {
87 std::ostringstream oss;
88 oss << "Prefilled queue with " << count << " requests";
89 log(GREEN, oss.str().c_str());
90 }
91}
92
94 if (requestDist_(rng_)) {
96
97 std::vector<int> blockedOctets;
98 if (!config_.blockedOctets.empty()) {
99 blockedOctets = config_.blockedOctets;
100 } else {
101 blockedOctets = {192, 255};
102 }
103
104 auto isBlockedFirstOctet = [](int octet, const std::vector<int>& blocked) -> bool {
105 return std::find(blocked.begin(), blocked.end(), octet) != blocked.end();
106 };
107
108 int firstOctet = std::stoi(req.ipIn.substr(0, req.ipIn.find('.')));
109 if (isBlockedFirstOctet(firstOctet, blockedOctets)) {
110 std::ostringstream oss;
111 oss << "Request blocked (bad octet) from " << req.ipIn;
113 log(RED, oss.str().c_str());
114 return;
115 }
116
117 else {
118 requestQueue_.push(req);
120 std::ostringstream oss;
121 oss << "New request added (" << req.jobType << ") from " << req.ipIn;
122 log(GREEN, oss.str().c_str());
123 }
124 }
125
126
127}
128
130 for (auto& server : servers_) {
131 if (!server->isBusy() && !requestQueue_.empty()) {
132 Request req = requestQueue_.front();
133 requestQueue_.pop();
134 server->assignRequest(req);
135 }
136 }
137}
138
140 int numServers = static_cast<int>(servers_.size());
141 int queueSize = static_cast<int>(requestQueue_.size());
142 minQueueThreshold_ = config_.minThreshold * numServers;
143 maxQueueThreshold_ = config_.maxThreshold * numServers;
144
145 if (currentTime_ - lastScaleTime_ < config_.scaleCooldown) return;
146
147 if (queueSize < minQueueThreshold_ && numServers > 1) {
148 auto it = std::find_if(servers_.begin(), servers_.end(),
149 [](const std::unique_ptr<WebServer>& s) { return !s->isBusy(); });
150 if (it != servers_.end()) {
151 servers_.erase(it);
154 std::ostringstream oss;
155 oss << "Removed server. Active: " << static_cast<int>(servers_.size())
156 << " | Added: " << serversAdded_ << " | Removed: " << serversRemoved_
157 << " (Queue: " << queueSize << ")";
158 log(YELLOW, oss.str().c_str());
159 }
160 } else if (queueSize > maxQueueThreshold_) {
161 servers_.push_back(std::make_unique<WebServer>(nextServerId_++));
164 std::ostringstream oss;
165 oss << "Added server. Active: " << static_cast<int>(servers_.size())
166 << " | Added: " << serversAdded_ << " | Removed: " << serversRemoved_
167 << " (Queue: " << queueSize << ")";
168 log(YELLOW, oss.str().c_str());
169 }
170}
171
172void LoadBalancer::run(int totalClockCycles) {
173 for (; currentTime_ < totalClockCycles; ++currentTime_) {
175
177
178 scaleServers();
179
180 if (config_.statusInterval > 0 && currentTime_ % config_.statusInterval == 0) {
181 std::ostringstream oss;
182 int active = static_cast<int>(servers_.size());
183 if (config_.colorsEnabled) {
184 oss << CYAN << "[" << RESET << BOLD << currentTime_ << RESET << CYAN << "]" << RESET
185 << " " << BOLD << "Active: " << active << RESET << " | "
186 << GREEN << "Added: " << serversAdded_ << RESET << " | "
187 << YELLOW << "Removed: " << serversRemoved_ << RESET << "\n";
188 } else {
189 oss << "[" << currentTime_ << "] Active: " << active
190 << " | Added: " << serversAdded_ << " | Removed: " << serversRemoved_ << "\n";
191 }
192 Logger::log(oss.str());
193 }
194
195 for (auto& server : servers_) {
196 server->process();
197 if (server->isFinished()) {
198 std::ostringstream oss;
199 oss << "Server " << server->getId() << " finished request";
200 log(MAGENTA, oss.str().c_str());
202 server->clear();
203 }
204 }
205
206
207 }
208
209}
210
211
212
213
214void LoadBalancer::printSummary(int initialServerCount) const {
215 int finalServerCount = servers_.size();
216 std::ostringstream oss;
217 if (config_.colorsEnabled) {
218 oss << "\n" << BOLD << "Simulation Summary:" << RESET << "\n"
219 << "Task time range: " << config_.minProcessingTime << " - " << config_.maxProcessingTime << "\n"
220 << MAGENTA << "Total Requests Processed: " << totalProcessed_ << RESET << "\n"
221 << YELLOW << "Total Requests still in Queue: " << requestQueue_.size() << RESET << "\n"
222 << GREEN << "Total Requests Added to Queue: " << requestsAddedToQueue_ << RESET << "\n"
223 << RED << "Total Requests Blocked: " << totalBlocked_ << RESET << "\n"
224 << GREEN << "Servers Added: " << serversAdded_ << RESET << "\n"
225 << YELLOW << "Servers Removed: " << serversRemoved_ << RESET << "\n"
226 << BOLD << "Started with " << initialServerCount << " servers" << RESET << "\n"
227 << BOLD << "Ended with " << finalServerCount << " servers" << RESET << "\n\n";
228 } else {
229 oss << "\nSimulation Summary:\n"
230 << "Task time range: " << config_.minProcessingTime << " - " << config_.maxProcessingTime << "\n"
231 << "Total Requests Processed: " << totalProcessed_ << "\n"
232 << "Total Requests still in Queue: " << requestQueue_.size() << "\n"
233 << "Total Requests Added to Queue: " << requestsAddedToQueue_ << "\n"
234 << "Total Requests Blocked: " << totalBlocked_ << "\n"
235 << "Servers Added: " << serversAdded_ << "\n"
236 << "Servers Removed: " << serversRemoved_ << "\n"
237 << "Started with " << initialServerCount << " servers\n"
238 << "Ended with " << finalServerCount << " servers\n\n";
239 }
240 Logger::log(oss.str());
241}
const char STREAMING_JOB
Job type character for streaming jobs.
const char PROCESS_JOB
Job type character for process jobs.
Defines the LoadBalancer class for the simulation.
Defines the Logger class for console and file output.
void generateRequest()
Possibly generate a new request (based on probability).
void scaleServers()
Add or remove servers based on queue size thresholds.
int minQueueThreshold_
std::uniform_int_distribution< int > timeDist_
int maxQueueThreshold_
std::string generateRandomIP()
Generate a random IP address string (x.x.x.x format).
LoadBalancerConfig config_
std::vector< std::unique_ptr< WebServer > > servers_
void assignRequests()
Assign queued requests to idle servers.
void log(const char *colorCode, const char *msg) const
Log a message with optional color code.
std::uniform_int_distribution< int > octetDist_
std::mt19937 rng_
void run(int totalClockCycles)
Run the simulation for the specified number of clock cycles.
LoadBalancer(int initialServers, const LoadBalancerConfig &config)
Construct the load balancer with initial servers and config.
void prefillQueue(int count)
Prefill the request queue with random requests.
std::uniform_int_distribution< int > jobTypeDist_
std::bernoulli_distribution requestDist_
void printSummary(int initialServerCount) const
Print simulation summary statistics.
std::queue< Request > requestQueue_
Request createRandomRequest()
Create a random request with random IPs and processing time.
int requestsAddedToQueue_
static void log(const std::string &message)
Write a message to both console and log file.
Definition Logger.cpp:36
Configuration parameters for the load balancer simulation.
Definition Config.h:16
Represents a single request/job in the load balancer simulation.
Definition Request.h:15
int timeRequired
Definition Request.h:18
std::string ipIn
Definition Request.h:16
char jobType
Definition Request.h:19
std::string ipOut
Definition Request.h:17