"Si un ouvrier veut bien faire son travail, il doit d'abord affûter ses outils." - Confucius, "Les Entretiens de Confucius. Lu Linggong"
Page de garde > La programmation > Construire un équilibreur de charge simple dans Go

Construire un équilibreur de charge simple dans Go

Publié le 2024-11-05
Parcourir:543

Les équilibreurs de charge sont cruciaux dans le développement de logiciels modernes. Si vous vous êtes déjà demandé comment les requêtes sont réparties sur plusieurs serveurs, ou pourquoi certains sites Web semblent plus rapides même en cas de trafic intense, la réponse réside souvent dans un équilibrage de charge efficace.

Building a simple load balancer in Go

Dans cet article, nous allons créer un équilibreur de charge d'application simple à l'aide de l'algorithme Round Robin dans Go. Le but de cet article est de comprendre comment fonctionne un équilibreur de charge sous le capot, étape par étape.

Qu'est-ce qu'un équilibreur de charge ?

Un équilibreur de charge est un système qui répartit le trafic réseau entrant sur plusieurs serveurs. Il garantit qu'aucun serveur ne supporte trop de charge, évitant ainsi les goulots d'étranglement et améliorant l'expérience utilisateur globale. L'approche d'équilibrage de charge garantit également qu'en cas de panne d'un serveur, le trafic peut être automatiquement redirigé vers un autre serveur disponible, réduisant ainsi l'impact de la panne et augmentant la disponibilité.

Pourquoi utilisons-nous des équilibreurs de charge ?

  • Haute disponibilité : en répartissant le trafic, les équilibreurs de charge garantissent que même en cas de panne d'un serveur, le trafic peut être acheminé vers d'autres serveurs sains, ce qui rend l'application plus résiliente.
  • Évolutivité : les équilibreurs de charge vous permettent de faire évoluer votre système horizontalement en ajoutant davantage de serveurs à mesure que le trafic augmente.
  • Efficacité : il maximise l'utilisation des ressources en garantissant que tous les serveurs partagent la charge de travail de manière égale.

Algorithmes d'équilibrage de charge

Il existe différents algorithmes et stratégies pour répartir le trafic :

  • Round Robin : l'une des méthodes les plus simples disponibles. Il répartit les requêtes de manière séquentielle entre les serveurs disponibles. Une fois qu'il atteint le dernier serveur, il recommence depuis le début.
  • Weighted Round Robin : similaire à l'algorithme de round robin, sauf que chaque serveur se voit attribuer une pondération numérique fixe. Ce poids donné est utilisé pour déterminer le serveur pour acheminer le trafic.
  • Moins de connexions : achemine le trafic vers le serveur avec le moins de connexions actives.
  • IP Hashing : sélectionnez le serveur en fonction de l'adresse IP du client.

Dans cet article, nous nous concentrerons sur la mise en œuvre d'un équilibreur de charge Round Robin.

Qu’est-ce qu’un algorithme Round Robin ?

Un algorithme round robin envoie chaque requête entrante au prochain serveur disponible de manière circulaire. Si le serveur A gère la première requête, le serveur B gérera la seconde et le serveur C gérera la troisième. Une fois que tous les serveurs ont reçu une requête, celle-ci redémarre depuis le serveur A.

Maintenant, passons au code et construisons notre équilibreur de charge !

Étape 1 : Définir l'équilibreur de charge et le serveur

type LoadBalancer struct {
    Current int
    Mutex   sync.Mutex
}

Nous allons d'abord définir une structure LoadBalancer simple avec un champ Current pour savoir quel serveur doit gérer la prochaine requête. Le Mutex garantit que notre code peut être utilisé simultanément en toute sécurité.

Chaque serveur que nous équilibrons est défini par la structure Server :

type Server struct {
    URL       *url.URL
    IsHealthy bool
    Mutex     sync.Mutex
}

Ici, chaque serveur a une URL et un indicateur IsHealthy, qui indique si le serveur est disponible pour traiter les requêtes.

Étape 2 : algorithme de tournoi à la ronde

Le cœur de notre équilibreur de charge est l'algorithme round robin. Voici comment cela fonctionne :

func (lb *LoadBalancer) getNextServer(servers []*Server) *Server {
    lb.Mutex.Lock()
    defer lb.Mutex.Unlock()

    for i := 0; i 


  • Cette méthode parcourt la liste des serveurs de manière circulaire. Si le serveur sélectionné est sain, il renvoie ce serveur pour gérer la demande entrante.
  • Nous utilisons Mutex pour garantir qu'un seul goroutine peut accéder et modifier le champ Current de l'équilibreur de charge à la fois. Cela garantit que l’algorithme round robin fonctionne correctement lorsque plusieurs requêtes sont traitées simultanément.
  • Chaque serveur possède également son propre Mutex. Lorsque nous vérifions le champ IsHealthy, nous verrouillons le Mutex du serveur pour empêcher l'accès simultané de plusieurs goroutines.
  • Sans verrouillage Mutex, il est possible qu'un autre goroutine modifie la valeur, ce qui pourrait entraîner la lecture de données incorrectes ou incohérentes.
  • Nous déverrouillons le Mutex dès que nous avons mis à jour le champ Current ou lu la valeur du champ IsHealthy pour garder la section critique aussi petite que possible. De cette façon, nous utilisons Mutex pour éviter toute condition de concurrence.

Étape 3 : configuration de l'équilibreur de charge

Notre configuration est stockée dans un fichier config.json, qui contient les URL du serveur et les intervalles de vérification de l'état (plus d'informations dans la section ci-dessous).

type Config struct {
    Port                string   `json:"port"`
    HealthCheckInterval string   `json:"healthCheckInterval"`
    Servers             []string `json:"servers"`
}

Le fichier de configuration pourrait ressembler à ceci :

{
  "port": ":8080",
  "healthCheckInterval": "2s",
  "servers": [
    "http://localhost:5001",
    "http://localhost:5002",
    "http://localhost:5003",
    "http://localhost:5004",
    "http://localhost:5005"
  ]
}

Étape 4 : bilans de santé

Nous voulons nous assurer que les serveurs sont sains avant de leur acheminer tout trafic entrant. Cela se fait en envoyant des contrôles de santé périodiques à chaque serveur :

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

Toutes les quelques secondes (comme spécifié dans la configuration), l'équilibreur de charge envoie une requête HEAD à chaque serveur pour vérifier s'il est sain. Si un serveur est en panne, l'indicateur IsHealthy est défini sur false, empêchant ainsi le trafic futur d'y être acheminé.

Étape 5 : Proxy inverse

Lorsque l'équilibreur de charge reçoit une requête, il la transmet au prochain serveur disponible à l'aide d'un proxy inverse. Dans Golang, le package httputil fournit un moyen intégré de gérer le proxy inverse, et nous l'utiliserons dans notre code via la fonction ReverseProxy :

func (s *Server) ReverseProxy() *httputil.ReverseProxy {
    return httputil.NewSingleHostReverseProxy(s.URL)
}
Qu’est-ce qu’un proxy inverse ?

Un proxy inverse est un serveur situé entre un client et un ou plusieurs serveurs backend. Il reçoit la demande du client, la transmet à l'un des serveurs principaux, puis renvoie la réponse du serveur au client. Le client interagit avec le proxy, ignorant quel serveur principal spécifique gère la demande.

Dans notre cas, l'équilibreur de charge agit comme un proxy inverse, assis devant plusieurs serveurs et distribuant les requêtes HTTP entrantes entre eux.

Étape 6 : Traitement des demandes

Lorsqu'un client envoie une requête à l'équilibreur de charge, il sélectionne le prochain serveur sain disponible à l'aide de l'implémentation de l'algorithme round robin dans la fonction getNextServer et transmet la requête du client à ce serveur. Si aucun serveur sain n'est disponible, nous envoyons une erreur de service indisponible au client.

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

La méthode ReverseProxy transmet la requête au serveur réel, et nous ajoutons également un en-tête personnalisé X-Forwarded-Server à des fins de débogage (bien qu'en production, nous devrions éviter d'exposer les détails internes du serveur comme celui-ci).

Étape 7 : Démarrage de l'équilibreur de charge

Enfin, nous démarrons l'équilibreur de charge sur le port spécifié :

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

Démo de travail

TL;DR

Dans cet article, nous avons construit un équilibreur de charge de base à partir de zéro dans Golang en utilisant un algorithme round robin. Il s'agit d'un moyen simple mais efficace de répartir le trafic sur plusieurs serveurs et de garantir que votre système peut gérer efficacement des charges plus élevées.

Il y a beaucoup plus à explorer, comme l'ajout de contrôles de santé sophistiqués, la mise en œuvre de différents algorithmes d'équilibrage de charge ou l'amélioration de la tolérance aux pannes. Mais cet exemple de base peut constituer une base solide sur laquelle s’appuyer.

Vous pouvez trouver le code source dans ce dépôt GitHub.

Déclaration de sortie Cet article est reproduit sur : https://dev.to/vivekalhat/building-a-simple-load-balancer-in-go-70d?1 En cas d'infraction, veuillez contacter [email protected] pour le supprimer.
Dernier tutoriel Plus>

Clause de non-responsabilité: Toutes les ressources fournies proviennent en partie d'Internet. En cas de violation de vos droits d'auteur ou d'autres droits et intérêts, veuillez expliquer les raisons détaillées et fournir une preuve du droit d'auteur ou des droits et intérêts, puis l'envoyer à l'adresse e-mail : [email protected]. Nous nous en occuperons pour vous dans les plus brefs délais.

Copyright© 2022 湘ICP备2022001581号-3