@@ -13,8 +13,19 @@ type LoadBalancer struct {
13
13
primaryGroup ServerGroup
14
14
backupGroup ServerGroup
15
15
primaryOnlineCount int
16
+ eventHandlerMtx sync.RWMutex
17
+ eventHandler EventHandler
16
18
}
17
19
20
+ type EventHandler func (eventType int , server * Server )
21
+
22
+ // -----------------------------------------------------------------------------
23
+
24
+ const (
25
+ ServerUpEvent int = iota + 1
26
+ ServerDownEvent
27
+ )
28
+
18
29
const (
19
30
InvalidParamsErr = "invalid parameter"
20
31
)
@@ -31,18 +42,30 @@ func Create() *LoadBalancer {
31
42
backupGroup : ServerGroup {
32
43
srvList : make ([]Server , 0 ),
33
44
},
45
+ eventHandlerMtx : sync.RWMutex {},
34
46
}
35
47
return & lb
36
48
}
37
49
50
+ // SetEventHandler sets a new notification handler callback
51
+ func (lb * LoadBalancer ) SetEventHandler (handler EventHandler ) {
52
+ lb .eventHandlerMtx .Lock ()
53
+ lb .eventHandler = handler
54
+ lb .eventHandlerMtx .Unlock ()
55
+ }
56
+
38
57
// Add adds a new server to the list
39
58
func (lb * LoadBalancer ) Add (opts ServerOptions , userData interface {}) error {
40
59
// Check options
41
60
if opts .Weight < 0 {
42
61
return errors .New (InvalidParamsErr )
43
62
}
44
63
if ! opts .IsBackup {
45
- if opts .MaxFails < 0 || opts .FailTimeout <= time .Duration (0 ) {
64
+ if opts .MaxFails > 0 {
65
+ if opts .FailTimeout <= time .Duration (0 ) {
66
+ return errors .New (InvalidParamsErr )
67
+ }
68
+ } else if opts .MaxFails < 0 {
46
69
return errors .New (InvalidParamsErr )
47
70
}
48
71
}
@@ -56,7 +79,7 @@ func (lb *LoadBalancer) Add(opts ServerOptions, userData interface{}) error {
56
79
if srv .opts .Weight == 0 {
57
80
srv .opts .Weight = 1
58
81
}
59
- if opts .IsBackup {
82
+ if opts .IsBackup || srv . opts . MaxFails == 0 {
60
83
srv .opts .MaxFails = 0
61
84
srv .opts .FailTimeout = time .Duration (0 )
62
85
}
@@ -89,21 +112,27 @@ func (lb *LoadBalancer) Add(opts ServerOptions, userData interface{}) error {
89
112
90
113
// Next gets the next available server. It can return nil if no available server
91
114
func (lb * LoadBalancer ) Next () * Server {
115
+ var nextServer * Server
116
+
92
117
now := time .Now ()
93
118
119
+ notifyUp := make ([]* Server , 0 ) // NOTE: We would use defer, but they are executed LIFO
120
+
94
121
// Lock access
95
122
lb .mtx .Lock ()
96
- defer lb .mtx .Unlock ()
97
123
98
124
// If all primary servers are offline, check if we can put someone up
99
125
if lb .primaryOnlineCount == 0 {
100
126
for idx := range lb .primaryGroup .srvList {
101
- if now .After (lb .primaryGroup .srvList [idx ].failTimestamp ) {
102
- // Put this server online again
103
- lb .primaryGroup .srvList [idx ].isDown = false
104
- lb .primaryGroup .srvList [idx ].failCounter = 0
127
+ srv := & lb .primaryGroup .srvList [idx ]
105
128
129
+ if now .After (srv .failTimestamp ) {
130
+ // Put this server online again
131
+ srv .isDown = false
132
+ srv .failCounter = 0
106
133
lb .primaryOnlineCount += 1
134
+
135
+ notifyUp = append (notifyUp , srv )
107
136
}
108
137
}
109
138
}
@@ -117,14 +146,17 @@ func (lb *LoadBalancer) Next() *Server {
117
146
// Set this server online again
118
147
srv .isDown = false
119
148
srv .lb .primaryOnlineCount += 1
149
+
150
+ notifyUp = append (notifyUp , srv )
120
151
}
121
152
122
153
if ! srv .isDown && lb .primaryGroup .currServerWeight < srv .opts .Weight {
123
154
// Got a server!
124
155
lb .primaryGroup .currServerWeight += 1
125
156
126
- // Done
127
- return srv
157
+ // Select this server
158
+ nextServer = srv
159
+ break
128
160
}
129
161
130
162
// Advance to next server
@@ -137,17 +169,18 @@ func (lb *LoadBalancer) Next() *Server {
137
169
}
138
170
}
139
171
140
- // If we reach here, there is no primary server available
141
- if len (lb .backupGroup .srvList ) > 0 {
172
+ // Look for backup servers if there is no primary available
173
+ if nextServer == nil && len (lb .backupGroup .srvList ) > 0 {
142
174
for {
143
175
srv := & lb .backupGroup .srvList [lb .backupGroup .currServerIdx ]
144
176
145
177
if lb .backupGroup .currServerWeight < srv .opts .Weight {
146
178
// Got a server!
147
179
lb .backupGroup .currServerWeight += 1
148
180
149
- // Done
150
- return srv
181
+ // Select this server
182
+ nextServer = srv
183
+ break
151
184
}
152
185
153
186
// Advance to next server
@@ -160,8 +193,16 @@ func (lb *LoadBalancer) Next() *Server {
160
193
}
161
194
}
162
195
163
- // No available server
164
- return nil
196
+ // Unlock access
197
+ lb .mtx .Unlock ()
198
+
199
+ // Call event callback
200
+ for _ , srv := range notifyUp {
201
+ lb .raiseEvent (ServerUpEvent , srv )
202
+ }
203
+
204
+ // Done
205
+ return nextServer
165
206
}
166
207
167
208
// WaitNext returns a channel that is fulfilled with the next available server
@@ -227,3 +268,14 @@ func (lb *LoadBalancer) WaitNext() (ch chan *Server) {
227
268
228
269
return
229
270
}
271
+
272
+ // -----------------------------------------------------------------------------
273
+ // Private methods
274
+
275
+ func (lb * LoadBalancer ) raiseEvent (eventType int , server * Server ) {
276
+ lb .eventHandlerMtx .RLock ()
277
+ if lb .eventHandler != nil {
278
+ lb .eventHandler (eventType , server )
279
+ }
280
+ lb .eventHandlerMtx .RUnlock ()
281
+ }
0 commit comments