"Si un trabajador quiere hacer bien su trabajo, primero debe afilar sus herramientas." - Confucio, "Las Analectas de Confucio. Lu Linggong"
Página delantera > Programación > Construyendo un balanceador de carga simple en Go

Construyendo un balanceador de carga simple en Go

Publicado el 2024-11-05
Navegar:337

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.

Building a simple load balancer in Go

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.

¿Qué es un equilibrador de carga?

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.

¿Por qué utilizamos balanceadores de carga?

  • Alta disponibilidad: Al distribuir el tráfico, los balanceadores de carga garantizan que incluso si un servidor falla, el tráfico se pueda enrutar a otros servidores en buen estado, lo que hace que la aplicación sea más resistente.
  • Escalabilidad: Los balanceadores de carga le permiten escalar su sistema horizontalmente agregando más servidores a medida que aumenta el tráfico.
  • Eficiencia: Maximiza la utilización de recursos al garantizar que todos los servidores compartan la carga de trabajo por igual.

Algoritmos de equilibrio de carga

Existen diferentes algoritmos y estrategias para distribuir el tráfico:

  • Round Robin: Uno de los métodos más simples disponibles. Distribuye solicitudes de forma secuencial entre los servidores disponibles. Una vez que llega al último servidor, comienza nuevamente desde el principio.
  • Round Robin ponderado: similar al algoritmo de round robin, excepto que a cada servidor se le asigna una ponderación numérica fija. Este peso determinado se utiliza para determinar el servidor para enrutar el tráfico.
  • Menos conexiones: dirige el tráfico al servidor con menos conexiones activas.
  • IP Hashing: seleccione el servidor según la dirección IP del cliente.

En esta publicación, nos centraremos en implementar un balanceador de carga Round Robin.

¿Qué es un algoritmo 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!

Paso 1: definir el equilibrador de carga y el servidor

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.

Paso 2: Algoritmo Round Robin

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 


  • Este método recorre la lista de servidores en forma circular. Si el servidor seleccionado está en buen estado, devuelve ese servidor para manejar la solicitud entrante.
  • Estamos utilizando Mutex para garantizar que solo una rutina pueda acceder y modificar el campo Actual del balanceador de carga a la vez. Esto garantiza que el algoritmo de operación por turnos funcione correctamente cuando se procesan varias solicitudes al mismo tiempo.
  • Cada servidor también tiene su propio Mutex. Cuando marcamos el campo IsHealthy, bloqueamos el Mutex del servidor para evitar el acceso simultáneo de múltiples gorutinas.
  • Sin el bloqueo Mutex, es posible que otra rutina esté cambiando el valor, lo que podría resultar en la lectura de datos incorrectos o inconsistentes.
  • Desbloqueamos Mutex tan pronto como actualizamos el campo Actual o leemos el valor del campo IsHealthy para mantener la sección crítica lo más pequeña posible. De esta manera, utilizamos Mutex para evitar cualquier condición de carrera.

Paso 3: configurar el equilibrador de carga

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"
  ]
}

Paso 4: controles de salud

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.

Paso 5: proxy inverso

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)
}
¿Qué es un proxy inverso?

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.

Paso 6: Manejo de solicitudes

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).

Paso 7: iniciar el equilibrador de carga

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())
}

Demostración de trabajo

TL;DR

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.

Declaración de liberación Este artículo se reproduce en: https://dev.to/vivekalhat/building-a-simple-load-balancer-in-go-70d?1 Si hay alguna infracción, comuníquese con [email protected] para eliminarla.
Último tutorial Más>

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