यदि आप उन सेवाओं को चला रहे हैं जो उच्च मात्रा में ट्रैफ़िक को संभालने में सक्षम होना चाहिए, तो आप अपने बैकेंड सर्वरों के बीच उस ट्रैफ़िक का एक बड़ा हिस्सा संतुलन लोड कर सकते हैं।
एक सरल एचटीटीटीपी लोड संतुलितर स्टैंडर्ड लाइब्रेरी का उपयोग करते हुए, इस कार्यान्वयन में, हम बैकेंड सर्वरों के एक संग्रह के बीच प्रवेश अनुरोधों को समान रूप से वितरित करने के लिए एक गोल रोबिन एल्गोरिथ्म का उपयोग करेंगे।
मूल संरचना
सबसे पहले, हमें अपने कोर डेटा संरचनाओं को परिभाषित करने की आवश्यकता है. हमारे लोड संतुलितर कई बैकेंड सर्वरों और उनके स्वास्थ्य को ट्रैक करेंगे:
package main
import (
"flag"
"fmt"
"log"
"net"
"net/http"
"net/http/httputil"
"net/url"
"sync"
"sync/atomic"
"time"
)
// Backend represents a backend server
type Backend struct {
URL *url.URL
Alive bool
mux sync.RWMutex
ReverseProxy *httputil.ReverseProxy
}
// SetAlive updates the alive status of backend
func (b *Backend) SetAlive(alive bool) {
b.mux.Lock()
b.Alive = alive
b.mux.Unlock()
}
// IsAlive returns true when backend is alive
func (b *Backend) IsAlive() (alive bool) {
b.mux.RLock()
alive = b.Alive
b.mux.RUnlock()
return
}
// LoadBalancer represents a load balancer
type LoadBalancer struct {
backends []*Backend
current uint64
}
// NextBackend returns the next available backend to handle the request
func (lb *LoadBalancer) NextBackend() *Backend {
// Simple round-robin
next := atomic.AddUint64(&lb.current, uint64(1)) % uint64(len(lb.backends))
// Find the next available backend
for i := 0; i < len(lb.backends); i++ {
idx := (int(next) + i) % len(lb.backends)
if lb.backends[idx].IsAlive() {
return lb.backends[idx]
}
}
return nil
}
अब यहां ध्यान में रखने के लिए कुछ महत्वपूर्ण बिंदु हैं:
- बैकेंड संरचना अपने URL और लाइव स्टेटस के साथ एकल बैकेंड सर्वर का प्रतिनिधित्व करती है।
- हम सुरक्षित रूप से एक साथ-साथ वातावरण में प्रत्येक बैकेंड की जीवित स्थिति को अपडेट करने और जांचने के लिए एक म्यूटेक्स का उपयोग कर रहे हैं।
- LoadBalancer बैकडेड की एक सूची को ट्रैक करता है और गोल-रोबिन एल्गोरिथ्म के लिए एक गिनती बनाए रखता है।
- हम परमाणु ऑपरेशन का उपयोग करते हैं ताकि एक साथ-साथ वातावरण में गिनती को सुरक्षित रूप से बढ़ाया जा सके।
- NextBackend विधि रोड-रोबिन एल्गोरिथ्म को लागू करती है, अस्वास्थ्यकर बैकेंड को छोड़ देती है।
स्वास्थ्य जांच
बैकेंड के अनावश्यकता का पता लगाना किसी भी लोड संतुलितर की महत्वपूर्ण कार्यों में से एक है। एक बहुत ही सरल स्वास्थ्य जांच तंत्र को लागू करता है:
// isBackendAlive checks whether a backend is alive by establishing a TCP connection
func isBackendAlive(u *url.URL) bool {
timeout := 2 * time.Second
conn, err := net.DialTimeout("tcp", u.Host, timeout)
if err != nil {
log.Printf("Site unreachable: %s", err)
return false
}
defer conn.Close()
return true
}
// HealthCheck pings the backends and updates their status
func (lb *LoadBalancer) HealthCheck() {
for _, b := range lb.backends {
status := isBackendAlive(b.URL)
b.SetAlive(status)
if status {
log.Printf("Backend %s is alive", b.URL)
} else {
log.Printf("Backend %s is dead", b.URL)
}
}
}
// HealthCheckPeriodically runs a routine health check every interval
func (lb *LoadBalancer) HealthCheckPeriodically(interval time.Duration) {
t := time.NewTicker(interval)
for {
select {
case <-t.C:
lb.HealthCheck()
}
}
}
यह स्वास्थ्य चेक सरल है:
- हम बैकेंड के साथ एक TCP कनेक्शन शुरू करने की कोशिश करते हैं।
- यदि यह कनेक्शन सफल होता है, तो बैकेंड जीवित है; अन्यथा यह मृत है।
- चेक HealthCheckPeriodically फ़ंक्शन में निर्दिष्ट अंतराल पर चलाया जाता है।
एक उत्पादन वातावरण में, आप शायद एक अधिक उन्नत स्वास्थ्य जांच चाहते हैं जो वास्तव में कुछ निर्दिष्ट अंत बिंदु पर एक एचटीटीटीपी अनुरोध करता है, लेकिन यह हमें शुरू करता है।
HTTP Handler का उपयोग करें
चलो आगे बढ़ते हैं और एचटीटीपी प्रबंधक को लागू करते हैं जो अनुरोध प्राप्त करेगा और उन्हें हमारे बैकेंड तक मार्गदर्शन करेगा:
// ServeHTTP implements the http.Handler interface for the LoadBalancer
func (lb *LoadBalancer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
backend := lb.NextBackend()
if backend == nil {
http.Error(w, "Service Unavailable", http.StatusServiceUnavailable)
return
}
// Forward the request to the backend
backend.ReverseProxy.ServeHTTP(w, r)
}
और यहां जादू होता है:
- हमारे गोल-रोबिन एल्गोरिथ्म हमें अगले उपलब्ध बैकेंड देता है।
- यदि कोई बैकडेड उपलब्ध नहीं है, तो हम एक 503 सेवा अनावश्यक त्रुटि वापस करते हैं।
- अन्यथा हम Go के आंतरिक विपरीत प्रॉक्सी के माध्यम से चयनित बैकेंड को अनुरोध प्रॉक्सी करते हैं।
ध्यान दें कि कैसेnet/http/httputil
पैकेज प्रदान करता हैReverseProxy
प्रकार, जो हमारे लिए HTTP अनुरोधों को प्रॉक्सी करने की सभी जटिलताओं को संभालता है।
सब कुछ एक साथ रखें
अंत में, हम इसे लागू करते हैंmain
हमारे लोड संतुलनर को कॉन्फ़िगर करने और शुरू करने के लिए कार्य:
func main() {
// Parse command line flags
port := flag.Int("port", 8080, "Port to serve on")
flag.Parse()
// Configure backends
serverList := []string{
"http://localhost:8081",
"http://localhost:8082",
"http://localhost:8083",
}
// Create load balancer
lb := LoadBalancer{}
// Initialize backends
for _, serverURL := range serverList {
url, err := url.Parse(serverURL)
if err != nil {
log.Fatal(err)
}
proxy := httputil.NewSingleHostReverseProxy(url)
proxy.ErrorHandler = func(w http.ResponseWriter, r *http.Request, err error) {
log.Printf("Error: %v", err)
http.Error(w, "Service Unavailable", http.StatusServiceUnavailable)
}
lb.backends = append(lb.backends, &Backend{
URL: url,
Alive: true,
ReverseProxy: proxy,
})
log.Printf("Configured backend: %s", url)
}
// Initial health check
lb.HealthCheck()
// Start periodic health check
go lb.HealthCheckPeriodically(time.Minute)
// Start server
server := http.Server{
Addr: fmt.Sprintf(":%d", *port),
Handler: &lb,
}
log.Printf("Load Balancer started at :%d\n", *port)
if err := server.ListenAndServe(); err != nil {
log.Fatal(err)
}
}
अब, मुख्य कार्य में क्या हो रहा है:
- हम कमांड लाइन ध्वज के आधार पर पोर्ट कॉन्फ़िगर करते हैं।
- हमने बैकेंड सर्वर की एक सूची बनाई है।
- For each backend server, we:
- Parse the URL
- Create a reverse proxy for that backend
- Add error handling for when a backend fails
- Add the backend to our load balancer
- हम एक प्रारंभिक स्वास्थ्य जांच करते हैं।
- हम नियमित स्वास्थ्य निगरानी के लिए एक goroutine शुरू करते हैं।
- अंत में, हम अपने लोड संतुलितर के साथ HTTP सर्वर को प्रबंधक के रूप में शुरू करते हैं।
लोड संतुलन का परीक्षण
तो फिर से हमारे लोड बैलेंसर का परीक्षण करने के लिए, हमें कुछ बैक एंड सर्वर की आवश्यकता है. एक बैक एंड सर्वर का एक बेवकूफ कार्यान्वयन इस तरह दिख सकता है:
// Save this to backend.go
package main
import (
"flag"
"fmt"
"log"
"net/http"
"os"
)
func main() {
port := flag.Int("port", 8081, "Port to serve on")
flag.Parse()
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
hostname, _ := os.Hostname()
fmt.Fprintf(w, "Backend server on port %d, host: %s, Request path: %s\n", *port, hostname, r.URL.Path)
})
log.Printf("Backend started at :%d\n", *port)
if err := http.ListenAndServe(fmt.Sprintf(":%d", *port), nil); err != nil {
log.Fatal(err)
}
}
विभिन्न पोर्टों पर इस बैकेंड के कई संदर्भ चलाएं:
go build -o backend backend.go
./backend -port 8081 &
./backend -port 8082 &
./backend -port 8083 &
फिर लोड संतुलन बनाने और चलाने के लिए:
go build -o load-balancer main.go
./load-balancer
अब, इसे परीक्षण करने के लिए कुछ अनुरोध करें:
curl http://localhost:8080/test
कई अनुरोध करें और आप उन्हें अपने बैकेंड सर्वरों पर एक गोल-रोबिन तरीके से वितरित किया जाता है देखेंगे।
संभावित सुधार
यह एक न्यूनतम कार्यान्वयन है, लेकिन आप इसे बेहतर बनाने के लिए कई चीजें कर सकते हैं:
-
Different balancing algorithms: Implement weighted round-robin, least connections, or IP hash-based selection.
-
Better health checking: Make full HTTP requests to a health endpoint instead of just checking TCP connectivity.
-
Metrics collection: Track request counts, response times, and error rates for each backend.
-
Sticky sessions: Ensure requests from the same client always go to the same backend.
-
TLS support: Add HTTPS for secure connections.
-
Dynamic configuration: Allow updating the backend list without restarting.
हमने एक सरल लेकिन प्रभावी HTTP लोड संतुलितर का निर्माण किया है, जो केवल Go की मानक पुस्तकालय का उपयोग करता है. यह उदाहरण नेटवर्क प्रोग्रामिंग में महत्वपूर्ण अवधारणाओं और Go के अंतर्निहित नेटवर्किंग सुविधाओं की शक्ति को दर्शाता है.
हालांकि यह उत्पादन के लिए तैयार नहीं है, यह समझने के लिए एक ठोस नींव प्रदान करता है कि लोड संतुलितर कैसे काम करते हैं. पूर्ण समाधान कोड के लगभग 150 पंक्तियां है - गॉ की अभिव्यक्तिजनकता और इसकी मानक पुस्तकालय की ताकत का एक सबूत।
उत्पादन के उपयोग के लिए, आप अधिक सुविधाओं का निर्माण करना चाहते हैं और मजबूत त्रुटि प्रबंधन जोड़ना चाहते हैं, लेकिन मुख्य अवधारणाएं सभी एक ही हैं. इन बुनियादी सिद्धांतों को समझने से आप किसी भी भार संतुलितर को बेहतर ढंग से कॉन्फ़िगर और डिबग करने के लिए तैयार होंगे जिसे आप सड़क पर उपयोग करेंगे।
आप यहां स्रोत कोड पा सकते हैंhttps://github.com/rezmoss/simple-load-balancer