Algoritmos de balanceamento: round robin, least connections e mais

O algoritmo de balanceamento é a regra que escolhe qual servidor recebe a próxima requisição. Round robin reveza em ordem, least connections prefere o menos ocupado e IP hash fixa cada cliente em um servidor.

Atualizado em 6 min de leitura

Objetivos de aprendizado

Depois de ler este artigo, você vai conseguir:

  • Entender como round robin, least connections, IP hash e hash consistente escolhem o servidor
  • Escolher o algoritmo certo para o tipo de tráfego da sua aplicação
  • Configurar cada algoritmo no nginx e simular a escolha em código
Nesta página

Ter vários servidores atrás de um load balancer resolve só metade do problema. A outra metade é decidir, a cada requisição, para qual deles ela vai. Essa decisão é o algoritmo de balanceamento, e ela muda a latência que o usuário sente, o quanto cada servidor trabalha e se uma sessão em memória sobrevive.

Cada algoritmo abaixo vem com a regra de escolha, o cenário em que ele falha e a configuração no nginx. No fim, um script Python curto simula a lógica para você testar.

requisiçõesIP do cliente1A2B3A4C5A6BRound robinapp-114app-225app-336Weighted round robinapp-11236peso 3app-24peso 1app-35peso 1Least connectionsapp-16app-2135app-324IP hashapp-126IP Bapp-2135IP Aapp-34IP Cconexões já ativasmesmo IP, mesmo servidor
A mesma fila cai em servidores diferentes conforme o algoritmo: por turno, por peso, pela carga ativa ou pelo IP do cliente.

Quais são os principais algoritmos de balanceamento?#

Algoritmo Como escolhe Ponto forte Ponto fraco
Round robin Reveza em ordem: 1, 2, 3, 1, 2, 3 Simples e previsível Ignora a carga real de cada servidor
Weighted round robin Reveza proporcionalmente ao peso Servidores de tamanhos diferentes Pesos são fixos, você ajusta à mão
Least connections Servidor com menos conexões ativas Absorve requisições de duração variada Precisa contar conexões em tempo real
IP hash Hash do IP do cliente Mesmo cliente, mesmo servidor Distribuição desigual atrás de NAT
Hash consistente Hash de uma chave (URL, cookie, IP) Poucas chaves mudam de servidor quando a lista muda Mais configuração
Random com duas escolhas Sorteia dois servidores e usa o menos ocupado Bom com vários balanceadores Menos intuitivo

Como funciona o round robin?#

O round robin é o mais simples: o balanceador mantém um ponteiro na lista de servidores e avança um a cada requisição. Com três servidores, a ordem é 1, 2, 3, 1, 2, 3. Ele não guarda estado sobre os servidores, por isso é barato e não tem surpresas.

Funciona bem quando os servidores têm o mesmo tamanho e as requisições têm custo parecido. Falha quando uma requisição ocupa um servidor por muito tempo: o round robin continua mandando trabalho novo para ele, porque só conta turnos, não carga.

No nginx, é o padrão. A documentação do nginx descreve o comportamento como round robin com pesos, e cada servidor tem peso 1 se você não disser o contrário.

Como o weighted round robin trata servidores diferentes?#

Se um servidor tem o dobro de memória, ele deve receber mais tráfego. O parâmetro weight faz isso. Com pesos 3, 1 e 1, de cada 5 requisições novas, 3 vão ao primeiro servidor e 1 a cada um dos outros:

nginx
upstream app {
    server 10.0.0.11:3000 weight=3;
    server 10.0.0.12:3000;
    server 10.0.0.13:3000;
}

Servidores sem weight ficam com o peso padrão, 1. É o mesmo exemplo da documentação do nginx.

Como o least connections escolhe o servidor?#

O balanceador acompanha quantas conexões ativas cada servidor tem e manda a próxima ao que tem menos. Se houver empate, desempata em rodízio. No nginx, a diretiva leva em conta os pesos dos servidores.

É a melhor escolha quando a duração das requisições varia: uploads, relatórios pesados, WebSockets ou chamadas a APIs lentas. Um servidor preso numa requisição longa acumula conexões e deixa de receber novas, enquanto os outros absorvem a fila. Para requisições todas curtas e iguais, o ganho sobre o round robin costuma ser pequeno.

nginx
upstream app {
    least_conn;
    server 10.0.0.11:3000;
    server 10.0.0.12:3000;
    server 10.0.0.13:3000;
}

Quando usar IP hash ou hash consistente?#

Nesses algoritmos a escolha depende de uma chave, não da carga. O mesmo valor de chave leva ao mesmo servidor. Isso serve para sessão em memória, cache local ou conexões que precisam voltar ao mesmo lugar.

O ip_hash do nginx usa os três primeiros octetos do IPv4 do cliente (ou o IPv6 inteiro) como chave:

nginx
upstream app {
    ip_hash;
    server 10.0.0.11:3000;
    server 10.0.0.12:3000;
    server 10.0.0.13:3000 down;
}

Há dois limites. O primeiro: como só os três primeiros octetos entram no hash, clientes da mesma rede /24, e todos os que passam pelo mesmo NAT, caem no mesmo servidor, o que desequilibra a carga. O segundo: se você remove um servidor, o hash é recalculado e muitos clientes mudam de destino. Por isso a documentação manda marcar o servidor temporariamente fora com down, o que preserva o mapeamento atual.

Para escolher outra chave, como a URL ou um cookie, use hash com consistent. O hash consistente (ketama) garante que, ao adicionar ou remover um servidor, só uma pequena parte das chaves muda de destino. É a escolha usual para caches, onde remapear tudo esvaziaria o cache de uma vez:

nginx
upstream cache {
    hash $request_uri consistent;
    server 10.0.0.21:8080;
    server 10.0.0.22:8080;
    server 10.0.0.23:8080;
}

Existem outros algoritmos?#

Sim. O nginx também oferece random, que sorteia um servidor respeitando os pesos. A variante random two least_conn sorteia dois servidores e fica com o que tem menos conexões ativas. Ela evita que vários balanceadores independentes mandem todos para o mesmo servidor ocioso ao mesmo tempo.

Existe ainda least_time, que prefere o servidor com menor tempo médio de resposta e menos conexões. Segundo a documentação, antes da versão 1.31.0 ele era exclusivo da assinatura comercial (NGINX Plus). Confira a sua versão antes de depender dele.

Como simular os algoritmos em código?#

Este script em Python 3 implementa round robin, round robin com pesos, least connections e hash por IP. Salve como lb.py e rode com python lb.py:

python
import hashlib
import itertools

SERVERS = ["app-1", "app-2", "app-3"]


def round_robin():
    return itertools.cycle(SERVERS)


def weighted_round_robin(weights):
    # {"app-1": 3, "app-2": 1} vira app-1, app-1, app-1, app-2
    return itertools.cycle([s for s, w in weights.items() for _ in range(w)])


def least_connections(active):
    # active: conexões abertas por servidor; o empate fica com o primeiro da lista
    return min(SERVERS, key=lambda s: active[s])


def ip_hash(client_ip):
    digest = hashlib.sha256(client_ip.encode()).digest()
    return SERVERS[int.from_bytes(digest[:4], "big") % len(SERVERS)]


rr = round_robin()
print([next(rr) for _ in range(6)])
wrr = weighted_round_robin({"app-1": 3, "app-2": 1, "app-3": 1})
print([next(wrr) for _ in range(5)])
print(least_connections({"app-1": 12, "app-2": 3, "app-3": 7}))
print(ip_hash("203.0.113.7") == ip_hash("203.0.113.7"))

A saída é ['app-1', 'app-2', 'app-3', 'app-1', 'app-2', 'app-3'], depois ['app-1', 'app-1', 'app-1', 'app-2', 'app-3'], depois app-2 (o menos ocupado) e True (mesmo IP, mesmo servidor). Adicione um quarto servidor a SERVERS e rode de novo: o ip_hash com módulo remapeia boa parte dos clientes, e é exatamente isso que o hash consistente evita. O weighted round robin deste script manda os três pedidos seguidos ao primeiro servidor; um balanceador real pode intercalar as escolhas.

Como escolher o algoritmo certo?#

  • Servidores iguais, requisições curtas e parecidas: round robin.
  • Servidores de tamanhos diferentes: weighted round robin.
  • Requisições longas ou de duração imprevisível: least connections.
  • Estado na memória do servidor que você não pode mover: IP hash. A melhor solução, porém, é tirar o estado da memória e guardar a sessão em Redis ou em um banco, e voltar a um algoritmo que não depende de chave.
  • Cache distribuído: hash consistente.

Em todos os casos, o balanceamento só funciona se os servidores forem intercambiáveis e o balanceador remover da rotação quem falha. O proxy reverso na frente deles é quem aplica a regra.

Como fazer isso na Square Cloud#

Na Square Cloud, o balanceamento é por domínio. Você sobe o mesmo código em várias aplicações e associa o mesmo domínio personalizado a todas; o subdomínio squareweb.app não entra no balanceamento. Você configura o domínio criando no seu registrador os registros DNS que o painel mostra (veja como usar um domínio personalizado).

O tráfego do domínio é distribuído em round robin entre as aplicações, com failover passivo (sem health check ativo) se uma delas ficar indisponível. Não há cobrança por requisição. O domínio personalizado começa no plano Standard, que permite até 2 aplicações por domínio. O Pro permite até 5 e o Enterprise, até 10.

Como o algoritmo é round robin e não há sticky session, cada requisição pode cair em uma aplicação diferente, e cada uma tem o próprio disco. Guarde sessões em um banco ou Redis, não na memória do processo. Veja o passo a passo em balanceamento de carga na Square Cloud.

Rede

Domínio próprio, TLS automático e CDN global para o seu app

Saiba mais

Perguntas frequentes

Qual o melhor algoritmo de balanceamento de carga?

Não existe um melhor para todos os casos. Round robin serve quando servidores e requisições são parecidos, least connections quando a duração das requisições varia muito, e IP hash ou hash consistente quando o mesmo cliente precisa voltar ao mesmo servidor.

Qual algoritmo o nginx usa por padrão?

Round robin com pesos. Se você não declarar nenhum método no bloco upstream, o nginx reveza entre os servidores, e cada um tem peso 1 por padrão.

Round robin e weighted round robin são diferentes?

Sim. No weighted round robin cada servidor recebe uma parcela proporcional ao seu peso. Com pesos 3, 1 e 1, de cada 5 requisições 3 vão ao primeiro servidor.

IP hash garante sessão fixa?

Quase. O mesmo IP cai no mesmo servidor enquanto a lista de servidores não muda. Se um servidor entra ou sai, parte dos clientes é remapeada, e clientes atrás do mesmo NAT dividem o mesmo destino.

Próximo artigoTimeouts e erros de load balancer: 502, 503 e 504 explicadosUm 504 significa que o load balancer desistiu de esperar o seu app; um 502, que recebeu uma resposta inválida. Veja cada timeout, como diagnosticar e corrigir.

Mais em Rede e domínios

Ver todos os 27 artigos