"Se um trabalhador quiser fazer bem o seu trabalho, ele deve primeiro afiar suas ferramentas." - Confúcio, "Os Analectos de Confúcio. Lu Linggong"
Primeira página > Programação > Construindo um balanceador de carga simples em Go

Construindo um balanceador de carga simples em Go

Publicado em 2024-11-05
Navegar:750

Os balanceadores de carga são cruciais no desenvolvimento de software moderno. Se você já se perguntou como as solicitações são distribuídas entre vários servidores ou por que certos sites parecem mais rápidos mesmo durante tráfego intenso, a resposta geralmente está no balanceamento de carga eficiente.

Building a simple load balancer in Go

Nesta postagem, construiremos um balanceador de carga de aplicativo simples usando o algoritmo Round Robin em Go. O objetivo desta postagem é entender como um balanceador de carga funciona nos bastidores, passo a passo.

O que é um balanceador de carga?

Um balanceador de carga é um sistema que distribui o tráfego de rede de entrada em vários servidores. Ele garante que nenhum servidor suporte muita carga, evitando gargalos e melhorando a experiência geral do usuário. A abordagem de balanceamento de carga também garante que, se um servidor falhar, o tráfego poderá ser automaticamente redirecionado para outro servidor disponível, reduzindo assim o impacto da falha e aumentando a disponibilidade.

Por que usamos balanceadores de carga?

  • Alta disponibilidade: Ao distribuir o tráfego, os balanceadores de carga garantem que, mesmo que um servidor falhe, o tráfego possa ser roteado para outros servidores íntegros, tornando o aplicativo mais resiliente.
  • Escalabilidade: Os balanceadores de carga permitem que você escale seu sistema horizontalmente adicionando mais servidores à medida que o tráfego aumenta.
  • Eficiência: maximiza a utilização de recursos, garantindo que todos os servidores compartilhem a carga de trabalho igualmente.

Algoritmos de balanceamento de carga

Existem diferentes algoritmos e estratégias para distribuir o tráfego:

  • Round Robin: Um dos métodos mais simples disponíveis. Distribui as solicitações sequencialmente entre os servidores disponíveis. Assim que chegar ao último servidor, ele começará novamente desde o início.
  • Round Robin Ponderado: Semelhante ao algoritmo round robin, exceto que cada servidor recebe uma ponderação numérica fixa. Este peso fornecido é usado para determinar o servidor para rotear o tráfego.
  • Least Connections: roteia o tráfego para o servidor com menos conexões ativas.
  • IP Hashing: Selecione o servidor com base no endereço IP do cliente.

Nesta postagem, vamos nos concentrar na implementação de um balanceador de carga Round Robin.

O que é um algoritmo Round Robin?

Um algoritmo round robin envia cada solicitação recebida para o próximo servidor disponível de maneira circular. Se o servidor A tratar a primeira solicitação, o servidor B tratará a segunda e o servidor C tratará a terceira. Depois que todos os servidores receberem uma solicitação, ela será reiniciada no servidor A.

Agora, vamos pular para o código e construir nosso balanceador de carga!

Etapa 1: definir o balanceador de carga e o servidor

type LoadBalancer struct {
    Current int
    Mutex   sync.Mutex
}

Primeiro definiremos uma estrutura LoadBalancer simples com um campo Current para controlar qual servidor deve lidar com a próxima solicitação. O Mutex garante que nosso código seja seguro para uso simultâneo.

Cada servidor que balanceamos a carga é definido pela estrutura do servidor:

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

Aqui, cada servidor tem uma URL e um sinalizador IsHealthy, que indica se o servidor está disponível para lidar com solicitações.

Etapa 2: Algoritmo Round Robin

O coração do nosso balanceador de carga é o algoritmo round robin. Veja como funciona:

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

    for i := 0; i 


  • Este método percorre a lista de servidores em um estilo round robin. Se o servidor selecionado estiver íntegro, ele retornará esse servidor para tratar a solicitação recebida.
  • Estamos usando Mutex para garantir que apenas uma goroutine possa acessar e modificar o campo Current do balanceador de carga por vez. Isso garante que o algoritmo round robin opere corretamente quando várias solicitações estiverem sendo processadas simultaneamente.
  • Cada servidor também possui seu próprio Mutex. Quando verificamos o campo IsHealthy, bloqueamos o Mutex do servidor para evitar o acesso simultâneo de várias goroutines.
  • Sem o bloqueio Mutex, é possível que outra goroutine esteja alterando o valor, o que pode resultar na leitura de dados incorretos ou inconsistentes.
  • Desbloqueamos o Mutex assim que atualizamos o campo Atual ou lemos o valor do campo IsHealthy para manter a seção crítica o menor possível. Desta forma, estamos usando Mutex para evitar qualquer condição de corrida.

Etapa 3: configurando o balanceador de carga

Nossa configuração é armazenada em um arquivo config.json, que contém os URLs do servidor e os intervalos de verificação de integridade (mais sobre isso na seção abaixo).

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

O arquivo de configuração pode ter esta aparência:

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

Etapa 4: verificações de integridade

Queremos ter certeza de que os servidores estão íntegros antes de rotear qualquer tráfego de entrada para eles. Isso é feito enviando verificações de integridade periódicas para 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()
    }
}

A cada poucos segundos (conforme especificado na configuração), o balanceador de carga envia uma solicitação HEAD para cada servidor para verificar se ele está íntegro. Se um servidor estiver inativo, o sinalizador IsHealthy será definido como falso, evitando que tráfego futuro seja roteado para ele.

Etapa 5: proxy reverso

Quando o balanceador de carga recebe uma solicitação, ele encaminha a solicitação para o próximo servidor disponível usando um proxy reverso. Em Golang, o pacote httputil fornece uma maneira integrada de lidar com proxy reverso, e vamos usá-lo em nosso código por meio da função ReverseProxy:

func (s *Server) ReverseProxy() *httputil.ReverseProxy {
    return httputil.NewSingleHostReverseProxy(s.URL)
}
O que é um proxy reverso?

Um proxy reverso é um servidor que fica entre um cliente e um ou mais servidores back-end. Ele recebe a solicitação do cliente, encaminha-a para um dos servidores back-end e, em seguida, retorna a resposta do servidor ao cliente. O cliente interage com o proxy, sem saber qual servidor back-end específico está lidando com a solicitação.

No nosso caso, o balanceador de carga atua como um proxy reverso, ficando na frente de vários servidores e distribuindo solicitações HTTP recebidas entre eles.

Etapa 6: Tratamento de solicitações

Quando um cliente faz uma solicitação ao balanceador de carga, ele seleciona o próximo servidor íntegro disponível usando a implementação do algoritmo round robin na função getNextServer e faz proxy da solicitação do cliente para esse servidor. Se nenhum servidor íntegro estiver disponível, enviaremos um erro de serviço indisponível ao 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)
    })

O método ReverseProxy faz proxy da solicitação para o servidor real e também adicionamos um cabeçalho personalizado X-Forwarded-Server para fins de depuração (embora na produção devamos evitar expor detalhes internos do servidor como este).

Etapa 7: iniciando o balanceador de carga

Finalmente, iniciamos o balanceador de carga na porta especificada:

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

Demonstração de trabalho

DR

Nesta postagem, construímos um balanceador de carga básico do zero em Golang usando um algoritmo round robin. Esta é uma maneira simples, mas eficaz, de distribuir o tráfego entre vários servidores e garantir que seu sistema possa lidar com cargas mais altas com eficiência.

Há muito mais para explorar, como adicionar verificações de integridade sofisticadas, implementar diferentes algoritmos de balanceamento de carga ou melhorar a tolerância a falhas. Mas este exemplo básico pode ser uma base sólida para construir.

Você pode encontrar o código-fonte neste repositório GitHub.

Declaração de lançamento Este artigo foi reproduzido em: https://dev.to/vivekalhat/building-a-simple-load-balancer-in-go-70d?1 Se houver alguma violação, entre em contato com [email protected] para excluí-la
Tutorial mais recente Mais>

Isenção de responsabilidade: Todos os recursos fornecidos são parcialmente provenientes da Internet. Se houver qualquer violação de seus direitos autorais ou outros direitos e interesses, explique os motivos detalhados e forneça prova de direitos autorais ou direitos e interesses e envie-a para o e-mail: [email protected]. Nós cuidaremos disso para você o mais rápido possível.

Copyright© 2022 湘ICP备2022001581号-3