mirror of
https://github.com/yusing/godoxy.git
synced 2026-04-24 01:08:31 +02:00
refactor(loadbalancer): implement sticky sessions and improve algorithm separation
- Refactor load balancer interface to separate server selection (ChooseServer) from request handling - Add cookie-based sticky session support with configurable max-age and secure cookie handling - Integrate idlewatcher requests with automatic sticky session assignment - Improve algorithm implementations: * Replace fnv with xxhash3 for better performance in IP hash and server keys * Add proper bounds checking and error handling in all algorithms * Separate concerns between server selection and request processing - Add Sticky and StickyMaxAge fields to LoadBalancerConfig - Create dedicated sticky.go for session management utilities
This commit is contained in:
@@ -20,9 +20,12 @@ import (
|
||||
// TODO: support weighted mode.
|
||||
type (
|
||||
impl interface {
|
||||
ServeHTTP(srvs types.LoadBalancerServers, rw http.ResponseWriter, r *http.Request)
|
||||
OnAddServer(srv types.LoadBalancerServer)
|
||||
OnRemoveServer(srv types.LoadBalancerServer)
|
||||
ChooseServer(srvs types.LoadBalancerServers, r *http.Request) types.LoadBalancerServer
|
||||
}
|
||||
customServeHTTP interface {
|
||||
ServeHTTP(srvs types.LoadBalancerServers, rw http.ResponseWriter, r *http.Request)
|
||||
}
|
||||
|
||||
LoadBalancer struct {
|
||||
@@ -235,7 +238,33 @@ func (lb *LoadBalancer) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
|
||||
gperr.LogWarn("failed to wake some servers", err, &lb.l)
|
||||
}
|
||||
}
|
||||
lb.impl.ServeHTTP(srvs, rw, r)
|
||||
|
||||
// Check for idlewatcher requests or sticky sessions
|
||||
if lb.Sticky || isIdlewatcherRequest(r) {
|
||||
if selectedServer := getStickyServer(r, srvs); selectedServer != nil {
|
||||
selectedServer.ServeHTTP(rw, r)
|
||||
return
|
||||
}
|
||||
// No sticky session, choose a server and set cookie
|
||||
selectedServer := lb.impl.ChooseServer(srvs, r)
|
||||
if selectedServer != nil {
|
||||
setStickyCookie(rw, r, selectedServer, lb.StickyMaxAge)
|
||||
selectedServer.ServeHTTP(rw, r)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if customServeHTTP, ok := lb.impl.(customServeHTTP); ok {
|
||||
customServeHTTP.ServeHTTP(srvs, rw, r)
|
||||
return
|
||||
}
|
||||
|
||||
selectedServer := lb.ChooseServer(srvs, r)
|
||||
if selectedServer == nil {
|
||||
http.Error(rw, "Service unavailable", http.StatusServiceUnavailable)
|
||||
return
|
||||
}
|
||||
selectedServer.ServeHTTP(rw, r)
|
||||
}
|
||||
|
||||
// MarshalJSON implements health.HealthMonitor.
|
||||
@@ -324,3 +353,22 @@ func (lb *LoadBalancer) availServers() []types.LoadBalancerServer {
|
||||
}
|
||||
return avail
|
||||
}
|
||||
|
||||
// isIdlewatcherRequest checks if this is an idlewatcher-related request
|
||||
func isIdlewatcherRequest(r *http.Request) bool {
|
||||
// Check for explicit idlewatcher paths
|
||||
if r.URL.Path == idlewatcher.WakeEventsPath ||
|
||||
r.URL.Path == idlewatcher.FavIconPath ||
|
||||
r.URL.Path == idlewatcher.LoadingPageCSSPath ||
|
||||
r.URL.Path == idlewatcher.LoadingPageJSPath {
|
||||
return true
|
||||
}
|
||||
|
||||
// Check if this is a page refresh after idlewatcher wake up
|
||||
// by looking for the sticky session cookie
|
||||
if _, err := r.Cookie("godoxy_lb_sticky"); err == nil {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user