Skip to content

Commit 4ff47dc

Browse files
author
SamSyntax
committed
proper healthcheck on servers and dynamic server rotation, improving logging
1 parent 7ded760 commit 4ff47dc

File tree

362 files changed

+196689
-49
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

362 files changed

+196689
-49
lines changed

application.log

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
INFO[2024-10-18T01:14:30+02:00] serving requests at localhost:7000 port=7000
2+
INFO[2024-10-18T01:14:31+02:00] forwarding to "http://localhost:8000"
3+
INFO[2024-10-18T01:14:32+02:00] forwarding to "http://localhost:8001"
4+
INFO[2024-10-18T01:14:33+02:00] forwarding to "http://localhost:8000"
5+
INFO[2024-10-18T01:14:34+02:00] forwarding to "http://localhost:8001"
6+
INFO[2024-10-18T01:14:35+02:00] forwarding to "http://localhost:8000"
7+
INFO[2024-10-18T01:14:35+02:00] Server 0 - addr: http://localhost:8000 is online
8+
INFO[2024-10-18T01:14:35+02:00] Server 1 - addr: http://localhost:8001 is online
9+
WARN[2024-10-18T01:14:35+02:00] Server 2 - addr: http://localhost:8002 is currently offline
10+
INFO[2024-10-18T01:14:36+02:00] forwarding to "http://localhost:8001"
11+
INFO[2024-10-18T01:14:37+02:00] forwarding to "http://localhost:8000"
12+
INFO[2024-10-18T01:14:38+02:00] forwarding to "http://localhost:8001"
13+
INFO[2024-10-18T01:14:39+02:00] forwarding to "http://localhost:8000"
14+
INFO[2024-10-18T01:14:40+02:00] forwarding to "http://localhost:8001"
15+
INFO[2024-10-18T01:14:40+02:00] Server 1 - addr: http://localhost:8001 is online
16+
INFO[2024-10-18T01:14:40+02:00] Server 0 - addr: http://localhost:8000 is online
17+
WARN[2024-10-18T01:14:40+02:00] Server 2 - addr: http://localhost:8002 is currently offline
18+
INFO[2024-10-18T01:14:41+02:00] forwarding to "http://localhost:8000"
19+
INFO[2024-10-18T01:14:42+02:00] forwarding to "http://localhost:8001"
20+
INFO[2024-10-18T01:14:43+02:00] forwarding to "http://localhost:8000"
21+
INFO[2024-10-18T01:14:44+02:00] forwarding to "http://localhost:8001"
22+
INFO[2024-10-18T01:14:45+02:00] forwarding to "http://localhost:8000"
23+
INFO[2024-10-18T01:14:45+02:00] Server 1 - addr: http://localhost:8001 is online
24+
INFO[2024-10-18T01:14:45+02:00] Server 0 - addr: http://localhost:8000 is online
25+
WARN[2024-10-18T01:14:45+02:00] Server 2 - addr: http://localhost:8002 is currently offline
26+
INFO[2024-10-18T01:14:46+02:00] forwarding to "http://localhost:8001"
27+
INFO[2024-10-18T01:14:47+02:00] forwarding to "http://localhost:8000"
28+
INFO[2024-10-18T01:14:48+02:00] forwarding to "http://localhost:8001"
29+
INFO[2024-10-18T01:14:49+02:00] forwarding to "http://localhost:8000"
30+
INFO[2024-10-18T01:14:50+02:00] forwarding to "http://localhost:8001"
31+
INFO[2024-10-18T01:14:50+02:00] Server 0 - addr: http://localhost:8000 is online
32+
INFO[2024-10-18T01:14:50+02:00] Server 1 - addr: http://localhost:8001 is online
33+
WARN[2024-10-18T01:14:50+02:00] Server 2 - addr: http://localhost:8002 is currently offline
34+
INFO[2024-10-18T01:14:51+02:00] forwarding to "http://localhost:8000"
35+
INFO[2024-10-18T01:14:52+02:00] forwarding to "http://localhost:8001"
36+
INFO[2024-10-18T01:14:53+02:00] forwarding to "http://localhost:8000"
37+
INFO[2024-10-18T01:14:54+02:00] forwarding to "http://localhost:8001"
38+
INFO[2024-10-18T01:14:55+02:00] forwarding to "http://localhost:8000"
39+
INFO[2024-10-18T01:14:55+02:00] Server 0 - addr: http://localhost:8000 is online
40+
INFO[2024-10-18T01:14:55+02:00] Server 1 - addr: http://localhost:8001 is online
41+
WARN[2024-10-18T01:14:55+02:00] Server 2 - addr: http://localhost:8002 is currently offline
42+
INFO[2024-10-18T01:14:56+02:00] forwarding to "http://localhost:8001"
43+
INFO[2024-10-18T01:14:57+02:00] forwarding to "http://localhost:8000"
44+
INFO[2024-10-18T01:14:58+02:00] forwarding to "http://localhost:8001"
45+
INFO[2024-10-18T01:14:59+02:00] forwarding to "http://localhost:8000"
46+
INFO[2024-10-18T01:15:00+02:00] Server 1 - addr: http://localhost:8001 is online
47+
WARN[2024-10-18T01:15:00+02:00] Server 2 - addr: http://localhost:8002 is currently offline
48+
INFO[2024-10-18T01:15:00+02:00] Server 0 - addr: http://localhost:8000 is online
49+
INFO[2024-10-18T01:15:00+02:00] forwarding to "http://localhost:8001"
50+
INFO[2024-10-18T01:15:01+02:00] forwarding to "http://localhost:8000"
51+
INFO[2024-10-18T01:15:02+02:00] forwarding to "http://localhost:8001"
52+
INFO[2024-10-18T01:15:03+02:00] forwarding to "http://localhost:8000"
53+
INFO[2024-10-18T01:15:04+02:00] forwarding to "http://localhost:8001"
54+
INFO[2024-10-18T01:15:05+02:00] Server 0 - addr: http://localhost:8000 is online
55+
INFO[2024-10-18T01:15:05+02:00] Server 1 - addr: http://localhost:8001 is online
56+
WARN[2024-10-18T01:15:05+02:00] Server 2 - addr: http://localhost:8002 is currently offline
57+
INFO[2024-10-18T01:15:05+02:00] forwarding to "http://localhost:8000"
58+
INFO[2024-10-18T01:15:06+02:00] forwarding to "http://localhost:8001"
59+
INFO[2024-10-18T01:15:07+02:00] forwarding to "http://localhost:8000"
60+
INFO[2024-10-18T01:15:08+02:00] forwarding to "http://localhost:8001"
61+
INFO[2024-10-18T01:15:09+02:00] forwarding to "http://localhost:8000"
62+
INFO[2024-10-18T01:15:10+02:00] Server 0 - addr: http://localhost:8000 is online
63+
INFO[2024-10-18T01:15:10+02:00] Server 1 - addr: http://localhost:8001 is online
64+
WARN[2024-10-18T01:15:10+02:00] Server 2 - addr: http://localhost:8002 is currently offline
65+
WARN[2024-10-18T01:15:15+02:00] Server 2 - addr: http://localhost:8002 is currently offline
66+
INFO[2024-10-18T01:15:15+02:00] Server 0 - addr: http://localhost:8000 is online
67+
INFO[2024-10-18T01:15:15+02:00] Server 1 - addr: http://localhost:8001 is online

go.mod

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,9 @@ module github.com/samsyntax/go-lb
22

33
go 1.22.7
44

5-
require gopkg.in/yaml.v3 v3.0.1
5+
require (
6+
github.com/sirupsen/logrus v1.9.3
7+
gopkg.in/yaml.v3 v3.0.1
8+
)
9+
10+
require golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 // indirect

go.sum

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,17 @@
1+
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
2+
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
3+
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
4+
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
5+
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
6+
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
7+
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
8+
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
9+
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
10+
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
11+
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 h1:0A+M6Uqn+Eje4kHMK80dtF3JCXC4ykBgQG4Fe06QRhQ=
12+
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
113
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
214
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
15+
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
316
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
417
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

loadbalancer.go

Lines changed: 57 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
11
package main
22

33
import (
4-
"fmt"
54
"net/http"
65
"net/http/httputil"
76
"net/url"
87
"os"
8+
"sync"
9+
"time"
10+
11+
log "github.com/sirupsen/logrus"
912
)
1013

1114
type ServerInterface interface {
@@ -20,51 +23,61 @@ type LbServer struct {
2023
name string
2124
weight int
2225
current int
26+
mu sync.Mutex
27+
alive bool
2328
}
2429

25-
func (s LbServer) Address() string {
30+
func (s *LbServer) Address() string {
2631
return s.addr
2732
}
2833

29-
func (s LbServer) IsAlive() bool {
30-
res, err := http.Get(s.addr)
34+
func (s *LbServer) IsAlive() bool {
35+
client := http.Client{
36+
Timeout: 5 * time.Second,
37+
}
38+
res, err := client.Get(s.addr)
3139
if err != nil {
32-
fmt.Printf("Server %s - addr: %s is currently offline\n", s.name, s.addr)
40+
log.Warnf("Server %s - addr: %s is currently offline\n", s.name, s.addr)
41+
s.alive = false
3342
return false
3443
}
3544
if res.StatusCode != http.StatusOK {
36-
fmt.Printf("Server %s - addr: %s is currently offline\n", s.name, s.addr)
45+
log.Warnf("Server %s - addr: %s is currently offline\n", s.name, s.addr)
46+
s.alive = false
3747
return false
3848
}
39-
fmt.Printf("Server %s - addr: %s is online\n", s.name, s.addr)
49+
s.alive = true
50+
log.Infof("Server %s - addr: %s is online\n", s.name, s.addr)
4051
return true
4152
}
4253

43-
func (s LbServer) Serve(w http.ResponseWriter, r *http.Request) {
54+
func (s *LbServer) Serve(w http.ResponseWriter, r *http.Request) {
4455
s.proxy.ServeHTTP(w, r)
4556
}
4657

4758
func NewLbServer(addr string, weight int) *LbServer {
4859
serverUrl, err := url.Parse(addr)
4960
if err != nil {
50-
fmt.Printf("Failed to parse url: %v\n", err)
61+
log.Panicf("Failed to parse url: %v\n", err)
5162
os.Exit(1)
5263
}
5364
return &LbServer{
5465
addr: addr,
5566
proxy: httputil.NewSingleHostReverseProxy(serverUrl),
5667
weight: weight,
68+
alive: true,
5769
}
5870
}
5971

6072
type LoadBalancer struct {
6173
port string
6274
roundRobinCount int
63-
servers []LbServer
75+
servers []*LbServer
6476
weighted bool
77+
mu sync.Mutex
6578
}
6679

67-
func NewLoadBalancer(port string, servers []LbServer, weighted bool) *LoadBalancer {
80+
func NewLoadBalancer(port string, servers []*LbServer, weighted bool) *LoadBalancer {
6881
return &LoadBalancer{
6982
port: port,
7083
roundRobinCount: 0,
@@ -74,40 +87,58 @@ func NewLoadBalancer(port string, servers []LbServer, weighted bool) *LoadBalanc
7487
}
7588

7689
func (lb *LoadBalancer) GetNextAvailableServer() LbServer {
90+
lb.mu.Lock()
91+
defer lb.mu.Unlock()
7792
if lb.weighted {
78-
return lb.getWeightedServer()
93+
return *lb.getWeightedServer()
7994
}
80-
return lb.getRoundRobinServer()
95+
return *lb.getRoundRobinServer()
8196
}
82-
func (lb *LoadBalancer) getWeightedServer() LbServer {
97+
func (lb *LoadBalancer) getWeightedServer() *LbServer {
8398

8499
totalServers := len(lb.servers)
85100
for i := 0; i < totalServers; i++ {
86-
server := &lb.servers[lb.roundRobinCount%totalServers]
87-
if server.current < server.weight && server.IsAlive() {
101+
server := lb.servers[lb.roundRobinCount%totalServers]
102+
if server.current < server.weight && server.alive {
103+
server.mu.Lock()
88104
server.current++
89-
return *server
105+
server.mu.Unlock()
106+
return server
90107
}
91108
server.current = 0
92109
lb.roundRobinCount++
93110
}
94111
lb.roundRobinCount++
95112
return lb.servers[lb.roundRobinCount%totalServers]
96113
}
97-
func (lb *LoadBalancer) getRoundRobinServer() LbServer {
98-
99-
server := lb.servers[lb.roundRobinCount%len(lb.servers)]
100-
for !server.IsAlive() {
114+
func (lb *LoadBalancer) getRoundRobinServer() *LbServer {
115+
totalServers := len(lb.servers)
116+
for i := 0; i < totalServers; i++ {
117+
server := lb.servers[lb.roundRobinCount%len(lb.servers)]
118+
if server.alive {
119+
lb.roundRobinCount++
120+
return server
121+
}
101122
lb.roundRobinCount++
102-
server = lb.servers[lb.roundRobinCount%len(lb.servers)]
103123
}
104-
105-
lb.roundRobinCount++
106-
return server
124+
return lb.servers[lb.roundRobinCount%len(lb.servers)]
107125
}
108126

109127
func (lb *LoadBalancer) ServeProxy(w http.ResponseWriter, r *http.Request) {
110128
targetServer := lb.GetNextAvailableServer()
111-
fmt.Printf("forwarding to %q\n", targetServer.Address())
129+
log.Infof("forwarding to %q\n", targetServer.Address())
112130
targetServer.Serve(w, r)
113131
}
132+
133+
func (lb *LoadBalancer) HealthCheck(interval time.Duration) {
134+
for _, server := range lb.servers {
135+
go func(s *LbServer) {
136+
ticker := time.NewTicker(interval)
137+
defer ticker.Stop()
138+
for {
139+
<-ticker.C
140+
s.IsAlive()
141+
}
142+
}(server)
143+
}
144+
}

loader.go

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,12 @@ package main
22

33
import (
44
"encoding/json"
5-
"fmt"
65
"io"
7-
"log"
86
"os"
97
"path/filepath"
108
"strconv"
119

10+
log "github.com/sirupsen/logrus"
1211
"gopkg.in/yaml.v3"
1312
)
1413

@@ -22,22 +21,28 @@ type ExternalServerYaml struct {
2221
Weight int `yaml:"weight"`
2322
}
2423

25-
func Loader(path string) []LbServer {
24+
func Loader(path string) []*LbServer {
2625
ext := filepath.Ext(path)
2726
switch ext {
2827
case ".yaml":
2928
res := ReadYaml(path)
29+
for _, s := range res {
30+
s.IsAlive()
31+
}
3032
return res
3133
case ".json":
3234
res := ReadJson(path)
35+
for _, s := range res {
36+
s.IsAlive()
37+
}
3338
return res
3439
default:
35-
fmt.Println("No file provided")
36-
return []LbServer{}
40+
log.Panic("No file provided")
41+
return []*LbServer{}
3742
}
3843
}
3944

40-
func ReadJson(path string) []LbServer {
45+
func ReadJson(path string) []*LbServer {
4146
file, err := os.Open(path)
4247
if err != nil {
4348
log.Fatalf("Failed to load servers from JSON file %v", err)
@@ -54,16 +59,16 @@ func ReadJson(path string) []LbServer {
5459
if err != nil {
5560
log.Fatalf("Failed to unmarshal JSON: %v", err)
5661
}
57-
res := make([]LbServer, 0, len(servers))
62+
res := make([]*LbServer, 0, len(servers))
5863
for k, s := range servers {
5964
lbServer := NewLbServer(s.Addr, s.Weight)
6065
lbServer.name = strconv.Itoa(k)
61-
res = append(res, *lbServer)
66+
res = append(res, lbServer)
6267
}
6368

6469
return res
6570
}
66-
func ReadYaml(path string) []LbServer {
71+
func ReadYaml(path string) []*LbServer {
6772
file, err := os.Open(path)
6873
if err != nil {
6974
log.Fatalf("Failed to load servers from JSON file %v", err)
@@ -80,11 +85,11 @@ func ReadYaml(path string) []LbServer {
8085
if err != nil {
8186
log.Fatalf("Failed to unmarshal YAML: %v", err)
8287
}
83-
res := make([]LbServer, 0, len(servers))
88+
res := make([]*LbServer, 0, len(servers))
8489
for k, s := range servers {
8590
lbServer := NewLbServer(s.Addr, s.Weight)
8691
lbServer.name = strconv.Itoa(k)
87-
res = append(res, *lbServer)
92+
res = append(res, lbServer)
8893
}
8994

9095
return res

local.yaml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
- addr: http://localhost:8000
3+
weight: 2
4+
- addr: http://localhost:8001
5+
weight: 1
6+
- addr: http://localhost:8002
7+
weight: 3

0 commit comments

Comments
 (0)