Los balanceadores de carga son cruciales en el desarrollo de software moderno. Si alguna vez se ha preguntado cómo se distribuyen las solicitudes entre varios servidores o por qué ciertos sitios web se sienten más rápidos incluso con mucho tráfico, la respuesta suele estar en un equilibrio de carga eficiente.
En esta publicación, crearemos un balanceador de carga de aplicaciones simple usando el algoritmo Round Robin en Go. El objetivo de esta publicación es comprender cómo funciona un balanceador de carga, paso a paso.
Un equilibrador de carga es un sistema que distribuye el tráfico de red entrante entre múltiples servidores. Garantiza que ningún servidor soporte demasiada carga, evitando cuellos de botella y mejorando la experiencia general del usuario. El enfoque de equilibrio de carga también garantiza que, si un servidor falla, el tráfico se pueda redirigir automáticamente a otro servidor disponible, lo que reduce el impacto de la falla y aumenta la disponibilidad.
Existen diferentes algoritmos y estrategias para distribuir el tráfico:
En esta publicación, nos centraremos en implementar un balanceador de carga Round Robin.
Un algoritmo de operación por turnos envía cada solicitud entrante al siguiente servidor disponible de forma circular. Si el servidor A maneja la primera solicitud, el servidor B manejará la segunda y el servidor C manejará la tercera. Una vez que todos los servidores han recibido una solicitud, se inicia nuevamente desde el servidor A.
¡Ahora, saltemos al código y construyamos nuestro balanceador de carga!
type LoadBalancer struct { Current int Mutex sync.Mutex }
Primero definiremos una estructura LoadBalancer simple con un campo Actual para realizar un seguimiento de qué servidor debe manejar la próxima solicitud. Mutex garantiza que nuestro código sea seguro de usar simultáneamente.
Cada servidor que cargamos balanceado está definido por la estructura del servidor:
type Server struct { URL *url.URL IsHealthy bool Mutex sync.Mutex }
Aquí, cada servidor tiene una URL y un indicador IsHealthy, que indica si el servidor está disponible para manejar solicitudes.
El corazón de nuestro balanceador de carga es el algoritmo de operación por turnos. Así es como funciona:
func (lb *LoadBalancer) getNextServer(servers []*Server) *Server { lb.Mutex.Lock() defer lb.Mutex.Unlock() for i := 0; i
Nuestra configuración se almacena en un archivo config.json, que contiene las URL del servidor y los intervalos de verificación de estado (más información en la sección siguiente).
type Config struct { Port string `json:"port"` HealthCheckInterval string `json:"healthCheckInterval"` Servers []string `json:"servers"` }
El archivo de configuración podría verse así:
{ "port": ":8080", "healthCheckInterval": "2s", "servers": [ "http://localhost:5001", "http://localhost:5002", "http://localhost:5003", "http://localhost:5004", "http://localhost:5005" ] }
Queremos asegurarnos de que los servidores estén en buen estado antes de enrutar cualquier tráfico entrante hacia ellos. Esto se hace enviando controles de estado periódicos a cada servidor:
func healthCheck(s *Server, healthCheckInterval time.Duration) { for range time.Tick(healthCheckInterval) { res, err := http.Head(s.URL.String()) s.Mutex.Lock() if err != nil || res.StatusCode != http.StatusOK { fmt.Printf("%s is down\n", s.URL) s.IsHealthy = false } else { s.IsHealthy = true } s.Mutex.Unlock() } }
Cada pocos segundos (como se especifica en la configuración), el balanceador de carga envía una solicitud HEAD a cada servidor para verificar si está en buen estado. Si un servidor no funciona, el indicador IsHealthy se establece en falso, lo que impide que el tráfico futuro se dirija a él.
Cuando el equilibrador de carga recibe una solicitud, la reenvía al siguiente servidor disponible mediante un proxy inverso. En Golang, el paquete httputil proporciona una forma integrada de manejar el proxy inverso y lo usaremos en nuestro código a través de la función ReverseProxy:
func (s *Server) ReverseProxy() *httputil.ReverseProxy { return httputil.NewSingleHostReverseProxy(s.URL) }
Un proxy inverso es un servidor que se encuentra entre un cliente y uno o más servidores backend. Recibe la solicitud del cliente, la reenvía a uno de los servidores backend y luego devuelve la respuesta del servidor al cliente. El cliente interactúa con el proxy, sin saber qué servidor backend específico está manejando la solicitud.
En nuestro caso, el equilibrador de carga actúa como un proxy inverso, situándose frente a varios servidores y distribuyendo las solicitudes HTTP entrantes entre ellos.
Cuando un cliente realiza una solicitud al equilibrador de carga, selecciona el siguiente servidor disponible en buen estado utilizando la implementación del algoritmo de operación por turnos en la función getNextServer y envía la solicitud del cliente a ese servidor. Si no hay ningún servidor en buen estado disponible, enviamos un error de servicio no disponible al cliente.
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { server := lb.getNextServer(servers) if server == nil { http.Error(w, "No healthy server available", http.StatusServiceUnavailable) return } w.Header().Add("X-Forwarded-Server", server.URL.String()) server.ReverseProxy().ServeHTTP(w, r) })
El método ReverseProxy envía la solicitud al servidor real y también agregamos un encabezado personalizado X-Forwarded-Server para fines de depuración (aunque en producción, debemos evitar exponer detalles internos del servidor como este).
Finalmente, iniciamos el balanceador de carga en el puerto especificado:
log.Println("Starting load balancer on port", config.Port) err = http.ListenAndServe(config.Port, nil) if err != nil { log.Fatalf("Error starting load balancer: %s\n", err.Error()) }
En esta publicación, creamos un balanceador de carga básico desde cero en Golang usando un algoritmo de operación por turnos. Esta es una forma simple pero efectiva de distribuir el tráfico entre múltiples servidores y garantizar que su sistema pueda manejar cargas más altas de manera eficiente.
Hay mucho más por explorar, como agregar controles de estado sofisticados, implementar diferentes algoritmos de equilibrio de carga o mejorar la tolerancia a fallas. Pero este ejemplo básico puede ser una base sólida sobre la que construir.
Puedes encontrar el código fuente en este repositorio de GitHub.
Descargo de responsabilidad: Todos los recursos proporcionados provienen en parte de Internet. Si existe alguna infracción de sus derechos de autor u otros derechos e intereses, explique los motivos detallados y proporcione pruebas de los derechos de autor o derechos e intereses y luego envíelos al correo electrónico: [email protected]. Lo manejaremos por usted lo antes posible.
Copyright© 2022 湘ICP备2022001581号-3