Skip to content

Commit 5fc4fe6

Browse files
author
SamSyntax
committed
Adding -path flag to read servers from config files
1 parent 5cfcc0e commit 5fc4fe6

25 files changed

+11677
-149
lines changed

README.md

Lines changed: 68 additions & 106 deletions
Original file line numberDiff line numberDiff line change
@@ -1,108 +1,53 @@
1-
Go Load Balancer with Dynamic Server Spawning
2-
This Go project implements a simple load balancer that forwards requests to multiple backend servers. The project dynamically spawns HTTP servers and balances the incoming requests using round-robin scheduling.
3-
4-
Overview
5-
The project consists of:
6-
7-
A server spawner that creates multiple backend servers listening on different ports.
8-
A load balancer that receives incoming requests on a designated port and forwards them to one of the spawned servers using a round-robin mechanism.
9-
Each server responds with a message indicating the port it is serving on.
10-
Features
11-
Dynamic server spawning with a given number of servers.
12-
Load balancing using the round-robin technique.
13-
Each server runs independently and responds with a simple message.
14-
Basic reverse proxy functionality for forwarding requests to backend servers.
15-
Project Structure
16-
Files
17-
main.go: The entry point of the application that spawns servers and starts the load balancer.
18-
loadbalancer.go: Contains the logic for the load balancer and the backend server configurations.
19-
Code Explanation
20-
Server Spawner
21-
The Spawner function spawns a specified number of backend HTTP servers. Each server is assigned a unique port and name. Servers are spawned concurrently in goroutines to avoid blocking the main thread.
22-
23-
```go
24-
func Spawner(amt int) []LbServer {
25-
servers := make([]LbServer, 0, amt)
26-
for i := 0; i < amt; i++ {
27-
name := fmt.Sprintf("Server %v", i)
28-
srv := Server(":"+strconv.Itoa(8000+i), name)
29-
servers = append(servers, srv)
30-
}
31-
return servers
32-
}
33-
```
34-
Server Initialization
35-
Each server is initialized using the Server function. It creates an HTTP server that listens on the given port and serves a simple response message.
36-
37-
```go
38-
func Server(port, name string) LbServer {
39-
srv := NewLbServer("http://localhost" + port)
40-
srv.name = name
41-
// Serve HTTP requests in a goroutine
42-
go func() {
43-
http.ListenAndServe(port, mux)
44-
}()
45-
return *srv
46-
}
47-
```
48-
Load Balancer
49-
The LoadBalancer struct holds a list of backend servers and distributes incoming requests among them using a round-robin algorithm.
50-
51-
```go
52-
func (lb *LoadBalancer) GetNextAvailableServer() LbServer {
53-
server := lb.servers[lb.roundRobinCount % len(lb.servers)]
54-
lb.roundRobinCount++
55-
return server
56-
}
57-
```
58-
The load balancer listens on a specified port (e.g., 7000), and forwards requests to the next available backend server.
59-
60-
```go
61-
func (lb *LoadBalancer) ServeProxy(w http.ResponseWriter, r *http.Request) {
62-
targetServer := lb.GetNextAvailableServer()
63-
fmt.Printf("forwarding to %q\n", targetServer.Address())
64-
targetServer.Serve(w, r)
65-
}
66-
```
67-
Running the Code
68-
Clone this repository or copy the source files into your Go workspace.
1+
# Go Load Balancer with Configurable Algorithms and External Server Configuration
692

70-
Run the code using:
3+
This Go project implements a load balancer that supports two balancing methods: Round Robin (`rr`) and Weighted Round Robin (`wrr`). It allows you to spawn local servers or read external server addresses from a configuration file (`.json` or `.yaml`) via a flag.
714

72-
```bash
73-
go build *.go -o ./lb && ./lb
74-
```
75-
or
5+
## Features
766

77-
```bash
78-
make run
79-
```
80-
The load balancer will listen on port 7000. Open your browser or use curl to send a request to localhost:7000:
7+
- **Round Robin** (`rr`) and **Weighted Round Robin** (`wrr`) algorithms for load balancing.
8+
- Support for **local server spawning** or **external server configuration** via `.json` or `.yaml` files.
9+
- Configurable via command-line flags.
10+
11+
## Command-Line Flags
12+
13+
- `-amount`: Number of local servers to spawn (used only with `-env local`). Default is `1234`.
14+
- `-method`: Load balancing method. Choose between:
15+
- `rr`: Round Robin.
16+
- `wrr`: Weighted Round Robin.
17+
- `-env`: Environment setting. Choose between:
18+
- `local`: Spawns the specified amount of local servers.
19+
- `external`: Reads server addresses from an external file (provided via `-path` flag).
20+
- `-path`: Specifies the path to the external `.json` or `.yaml` configuration file (used with `-env external`).
21+
22+
### Example Usage
23+
24+
1. **Spawn Local Servers** (5 servers, round-robin method):
25+
```bash
26+
go run *.go -amount 5 -method rr -env local
27+
Use External Servers from a JSON File (weighted round-robin method):
8128

8229
```bash
83-
curl http://localhost:7000
30+
go run *.go -method wrr -env external -path ./servers.json
8431
```
85-
The request will be forwarded to one of the backend servers, and you will see a response indicating the server's port.
32+
Use External Servers from a YAML File (round-robin method):
8633

87-
We can also specify amount of servers and balancing method (Round Robin and Weighted Round Robin) by building program and passing flags:
8834
```bash
89-
make build && ./lb -amount 5 -method rr
35+
go run *.go -method rr -env external -path ./servers.yaml
9036
```
91-
Example Output
92-
```bash Serving requests at localhost:7000
93-
Spawning server: Server 0 at localhost:8000
94-
Spawning server: Server 1 at localhost:8001
95-
Spawning server: Server 2 at localhost:8002
96-
Spawning server: Server 3 at localhost:8003
97-
Spawning server: Server 4 at localhost:8004
98-
forwarding to "localhost:8001"
99-
forwarding to "localhost:8002"
100-
forwarding to "localhost:8003"
101-
forwarding to "localhost:8004"
102-
forwarding to "localhost:8000"
37+
### Configuration File Format
38+
When using the -env external flag, the load balancer will read server information from a configuration file. You can provide the file in either YAML or JSON format.
39+
40+
# Sample YAML Configuration (servers.yaml)
41+
```yaml
42+
---
43+
- addr: https://facebook.com
44+
weight: 2
45+
- addr: https://framed-designs.com
46+
weight: 1
47+
- addr: https://google.com
48+
weight: 3
10349
```
104-
You can also use the loadbalancer to redirect traffic to external servers. Create <b>servers.json</b> in the root directory
105-
of the project and specify servers (add weights if you want to use Weighted Round Robin).
50+
# Sample JSON Configuration (servers.json)
10651
```json
10752
[
10853
{
@@ -119,17 +64,34 @@ of the project and specify servers (add weights if you want to use Weighted Roun
11964
}
12065
]
12166
```
122-
Then simply pass <b>-env</b> flag. It accepts "local" and "external" where "local" will start local servers
123-
and "external" will read the JSON file.
67+
# How It Works
68+
## Load Balancing Methods
69+
Round Robin (rr): Distributes requests evenly across all available servers.
70+
Weighted Round Robin (wrr): Distributes requests based on the weight assigned to each server. Servers with higher weights receive more traffic.
71+
Local Server Spawning
72+
When using -env local, the program spawns a number of local servers on ports starting from 8000 (e.g., localhost:8000, localhost:8001, etc.).
73+
74+
## External Servers
75+
When using -env external with the -path flag, the load balancer reads external server addresses from the specified JSON or YAML file and balances requests accordingly.
76+
77+
## Example Output
78+
Example output when running with 3 local servers:
12479

12580
```bash
126-
make build && ./lb -method wrr -env external
81+
serving requests at localhost:7000
82+
forwarding to "localhost:8000"
83+
forwarding to "localhost:8001"
84+
forwarding to "localhost:8002"
85+
```
86+
Example output when using an external JSON configuration:
87+
88+
```bash
89+
serving requests at localhost:7000
90+
forwarding to "https://facebook.com"
91+
forwarding to "https://twitch.tv"
92+
forwarding to "https://google.com"
12793
```
128-
Customization
129-
Number of Servers: You can modify the number of servers spawned by changing the <b>-amount</b> flag value.
130-
Ports: The backend servers listen on ports 8000 and higher. You can modify the port range in the Spawner function.
131-
Dependencies
132-
This project does not require any third-party dependencies. It only uses the Go standard library.
133-
134-
License
135-
This project is licensed under the MIT License.
94+
95+
# License
96+
This project is open-source and available under the MIT License.
97+

cfg.yaml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
- addr: https://facebook.com
3+
weight: 2
4+
- addr: https://twitch.tv
5+
weight: 1
6+
- addr: https://google.com
7+
weight: 3

go.mod

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
11
module github.com/samsyntax/go-lb
22

33
go 1.22.7
4+
5+
require gopkg.in/yaml.v3 v3.0.1

go.sum

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
2+
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
3+
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
4+
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

json.go

Lines changed: 0 additions & 41 deletions
This file was deleted.

loadbalancer

-7.51 MB
Binary file not shown.

loader.go

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
package main
2+
3+
import (
4+
"encoding/json"
5+
"fmt"
6+
"io"
7+
"log"
8+
"os"
9+
"path/filepath"
10+
"strconv"
11+
12+
"gopkg.in/yaml.v3"
13+
)
14+
15+
type ExternalServerJson struct {
16+
Addr string `json:"addr"`
17+
Weight int `json:"weight"`
18+
}
19+
20+
type ExternalServerYaml struct {
21+
Addr string `yaml:"addr"`
22+
Weight int `yaml:"weight"`
23+
}
24+
25+
func Loader(path string) []LbServer {
26+
ext := filepath.Ext(path)
27+
switch ext {
28+
case ".yaml":
29+
res := ReadYaml(path)
30+
return res
31+
case ".json":
32+
res := ReadJson(path)
33+
return res
34+
default:
35+
fmt.Println("No file provided")
36+
return []LbServer{}
37+
}
38+
}
39+
40+
func ReadJson(path string) []LbServer {
41+
file, err := os.Open(path)
42+
if err != nil {
43+
log.Fatalf("Failed to load servers from JSON file %v", err)
44+
}
45+
defer file.Close()
46+
47+
byteVal, err := io.ReadAll(file)
48+
if err != nil {
49+
log.Fatalf("Failed to read file: %v", err)
50+
}
51+
52+
var servers []ExternalServerJson
53+
err = json.Unmarshal(byteVal, &servers)
54+
if err != nil {
55+
log.Fatalf("Failed to unmarshal JSON: %v", err)
56+
}
57+
res := make([]LbServer, 0, len(servers))
58+
for k, s := range servers {
59+
lbServer := NewLbServer(s.Addr, s.Weight)
60+
lbServer.name = strconv.Itoa(k)
61+
res = append(res, *lbServer)
62+
}
63+
64+
return res
65+
}
66+
func ReadYaml(path string) []LbServer {
67+
file, err := os.Open(path)
68+
if err != nil {
69+
log.Fatalf("Failed to load servers from JSON file %v", err)
70+
}
71+
defer file.Close()
72+
73+
byteVal, err := io.ReadAll(file)
74+
if err != nil {
75+
log.Fatalf("Failed to read file: %v", err)
76+
}
77+
78+
var servers []ExternalServerYaml
79+
err = yaml.Unmarshal(byteVal, &servers)
80+
if err != nil {
81+
log.Fatalf("Failed to unmarshal YAML: %v", err)
82+
}
83+
res := make([]LbServer, 0, len(servers))
84+
for k, s := range servers {
85+
lbServer := NewLbServer(s.Addr, s.Weight)
86+
lbServer.name = strconv.Itoa(k)
87+
res = append(res, *lbServer)
88+
}
89+
90+
return res
91+
}

main.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,13 @@ func main() {
1111
nFlag := flag.Int("amount", 1234, "Enter amount of local servers to be spawned")
1212
method := flag.String("method", "rr", "Load balancing method: 'rr - Round Robin | wrr - Weighted Round Robin")
1313
env := flag.String("env", "local", "Specify wether local servers should be started or provide JSON file with addresses of external servers. ")
14+
path := flag.String("path", "./servers.yaml", "Specify a path to servers config file. Either yaml or json. ")
15+
1416
flag.Parse()
15-
var servers []LbServer
17+
var servers []LbServer
1618
switch *env {
1719
case "external":
18-
servers = ReadJson()
20+
servers = Loader(*path)
1921
case "local":
2022
servers = Spawner(*nFlag)
2123
}

0 commit comments

Comments
 (0)