O que é um load balancer: como distribuir tráfego sem derrubar o sistema
Load balancer distribui requisições entre vários servidores; conheça algoritmos, health checks, L4 vs L7, sessão fixa e por que ele elimina o ponto único de falha.
Neste artigo
Todo sistema que cresce chega numa bifurcação: um único servidor não aguenta mais o tráfego, ou não pode mais parar sem tirar o produto do ar. As duas dores têm a mesma raiz — depender de uma máquina só — e a mesma solução estrutural: colocar mais de um servidor rodando a aplicação e alguém na frente decidindo, requisição a requisição, para qual deles mandar cada pedido. Esse "alguém" é o load balancer, e entender como ele decide, o que ele observa e o que ele não resolve sozinho é um dos pontos de virada mais importantes na engenharia de um sistema que precisa aguentar carga real.
Este artigo explica o load balancer do zero: por que ele existe, os benefícios concretos que ele traz, os algoritmos de distribuição, o papel central dos health checks, a diferença entre balancear na camada de transporte e na camada de aplicação, e o problema — recorrente e mal resolvido na maioria dos sistemas — de o que fazer com o estado da sessão quando o cliente pode cair em qualquer servidor a cada requisição.
O problema: um servidor tem teto, e é um ponto único de falha#
Toda máquina tem um limite de capacidade. CPU, memória, conexões simultâneas, banda de rede — em algum ponto, mais requisições do que a máquina consegue processar por segundo simplesmente enfileiram, e a latência sobe até o sistema parecer travado. Não existe servidor infinito; existe servidor que ainda não encontrou seu limite.
E mesmo antes de bater no teto, um servidor único é um ponto único de falha. Se é o único que atende as requisições e ele cai — pane de hardware, deploy que quebrou, esgotamento de memória, uma dependência externa que trava o processo —, o sistema inteiro cai junto. Não importa quão bem escrito seja o código: rodando em uma única instância, qualquer incidente naquela máquina é um incidente no serviço inteiro. É a definição literal de fragilidade — um ponto cuja falha derruba tudo que depende dele.
A resposta óbvia para o teto de capacidade é dar mais capacidade ao servidor: mais CPU, mais RAM, um disco mais rápido. Isso é escalar verticalmente, e funciona — até não funcionar mais. Toda linha de máquinas tem um topo (a maior instância que a nuvem oferece, o maior servidor que o orçamento permite), o custo cresce mais rápido que a capacidade a partir de um certo ponto, e o problema do ponto único de falha continua intocado: uma máquina maior ainda é uma máquina só. Escalar verticalmente empurra o teto para mais longe; não o remove, e não resolve a fragilidade.
Escalar horizontalmente é a alternativa estrutural: em vez de uma máquina maior, várias máquinas do mesmo tamanho. Em teoria, dobrar o número de servidores dobra a capacidade, e o custo cresce de forma linear e previsível em vez de exponencial. Mas escalar horizontalmente introduz uma pergunta que escalar verticalmente nunca precisou responder: se agora há cinco servidores idênticos rodando a mesma aplicação, qual deles atende cada requisição que chega? O cliente não pode escolher — ele só conhece um endereço. Alguém precisa ficar entre o cliente e o pool de servidores, recebendo cada pedido e decidindo, na hora, para onde mandá-lo. Esse componente é o load balancer, e sem ele escalar horizontalmente não é possível na prática — só na teoria.
O que é, conceitualmente#
Um load balancer é um intermediário que fica entre os clientes e um conjunto — chamado de pool ou upstream — de servidores backend que fazem o mesmo trabalho. Do lado de fora, o sistema continua parecendo um único destino: um domínio, um IP, uma porta. Do lado de dentro, cada requisição que chega nesse endereço único é repartida para um dos servidores do pool, segundo alguma regra de distribuição.
Essa fachada única é a parte mais subestimada do papel do load balancer. O cliente nunca sabe, nem precisa saber, quantos servidores existem por trás do endereço que ele chama, nem qual deles atendeu sua requisição específica. Isso significa que o número de servidores no pool pode mudar — crescer numa Black Friday, encolher de madrugada, perder um servidor doente, ganhar um servidor novo — sem que nada mude do ponto de vista de quem consome o sistema. A elasticidade do backend fica inteiramente escondida atrás de uma interface estável.
Na prática, um load balancer típico mantém uma lista dos servidores saudáveis no pool, aplica um algoritmo para escolher um deles a cada requisição (ou conexão, dependendo da camada), encaminha o tráfego, e devolve a resposta ao cliente como se tivesse vindo dele mesmo. Tudo isso acontece de forma transparente e, num sistema bem dimensionado, com latência adicional mínima.
Os benefícios concretos#
Alta disponibilidade. Este é o ganho mais direto: se um servidor do pool cai, o load balancer para de mandar tráfego para ele e continua atendendo com os que restam. O sistema perde capacidade, mas não perde disponibilidade — não há mais um ponto único cuja falha derruba tudo. Quanto maior o pool, menor a fração de capacidade que a perda de um único servidor representa.
Escala horizontal de verdade. Como o cliente só enxerga o endereço do load balancer, adicionar ou remover servidores do pool é uma operação interna, invisível de fora. Isso é o que permite escalar sob demanda — adicionar máquinas quando o tráfego sobe, removê-las quando cai — sem reconfigurar nada do lado do cliente e sem downtime na transição.
Melhor desempenho por distribuição uniforme. Sem um load balancer, não há garantia de que a carga se espalhe de forma equilibrada entre os servidores — na prática, sem coordenação, ela nem se espalha. Com ele, o algoritmo de distribuição existe justamente para evitar que um servidor fique sobrecarregado enquanto outro fica ocioso, o que mantém a latência mais previsível para todo mundo.
Manutenção sem derrubar o serviço. Isso é o que torna o rolling deploy possível. Para atualizar um servidor, o operador o tira do pool ("drena" as conexões em andamento, para de mandar tráfego novo para ele), aplica a atualização, testa, e devolve ao pool — um de cada vez, ou em lotes pequenos, enquanto o restante do pool continua atendendo normalmente. O usuário final nunca percebe que houve manutenção.
Algoritmos de distribuição#
A pergunta "qual servidor atende esta requisição?" tem várias respostas possíveis, cada uma com um cenário onde é a escolha certa.
Round Robin. O mais simples: os servidores são revezados em círculo — o primeiro pedido vai para o servidor 1, o segundo para o 2, o terceiro para o 3, o quarto volta para o 1, e assim por diante. Fácil de implementar e previsível, funciona bem quando os servidores têm capacidade equivalente e as requisições têm custo de processamento parecido entre si. A limitação aparece quando isso não é verdade: se uma requisição é muito mais pesada que outra, Round Robin pode empilhar trabalho pesado num servidor que já estava ocupado, só porque era a vez dele.
Weighted Round Robin. A mesma ideia de revezamento, mas com pesos: um servidor mais potente recebe proporcionalmente mais requisições que um mais fraco. Se o servidor A tem peso 3 e o B tem peso 1, A recebe três requisições para cada uma que B recebe. Resolve o caso comum de um pool com máquinas de capacidades diferentes — típico quando se expande a infraestrutura aos poucos, em vez de trocar tudo de uma vez.
Least Connections. Em vez de revezar cegamente, o load balancer manda a próxima requisição para o servidor que, neste momento, tem menos conexões ativas. É sensivelmente melhor que Round Robin quando as requisições têm duração muito variável — um servidor pode estar processando uma operação longa enquanto os outros já terminaram várias curtas, e Least Connections reage a essa realidade em vez de ignorá-la.
Least Response Time. Uma evolução de Least Connections que também considera o tempo de resposta recente de cada servidor, não só o número de conexões abertas. Tende a favorecer servidores que estão respondendo rápido no momento, o que captura degradação de desempenho (um servidor lento por sobrecarga de CPU, por exemplo) que a contagem de conexões sozinha não enxerga.
IP Hash / consistent hashing. Em vez de olhar para a carga dos servidores, esse algoritmo calcula um hash de algum atributo do cliente — tipicamente o IP de origem — e usa esse hash para decidir o servidor. O resultado é que o mesmo cliente cai, de forma consistente, sempre no mesmo servidor, enquanto o pool não muda. Isso é útil quando existe alguma razão para "grudar" o cliente num servidor específico — geralmente porque há estado guardado ali, um problema que a próxima seção detalha. Consistent hashing é o refinamento que faz isso funcionar bem mesmo quando o pool muda de tamanho: sem ele, adicionar ou remover um servidor reembaralharia quase todos os mapeamentos cliente→servidor de uma vez; com ele, só uma fração pequena muda.
Nenhum desses algoritmos é universalmente "o melhor" — a escolha depende do perfil de carga, da uniformidade dos servidores e de se existe ou não uma razão para afinidade entre cliente e servidor.
Health checks: o coração da disponibilidade#
De nada adianta um algoritmo de distribuição sofisticado se o load balancer continuar mandando tráfego para um servidor que já caiu. É o health check que fecha esse ciclo, e é ele que transforma "vários servidores atrás de um endereço" em "alta disponibilidade" de fato.
Health check ativo: o load balancer pergunta. Periodicamente — a cada poucos segundos, tipicamente — o load balancer faz uma requisição a um endpoint dedicado em cada servidor do pool, normalmente algo como /health ou /healthz, e espera uma resposta rápida indicando que o processo está de pé e capaz de atender tráfego real. Um bom endpoint de health check verifica minimamente que as dependências críticas do servidor (banco, cache) estão acessíveis, não só que o processo HTTP está respondendo — um servidor que responde 200 no /health mas não consegue falar com o banco é um falso positivo perigoso.
Health check passivo: o load balancer observa. Em vez de perguntar proativamente, o load balancer acompanha as respostas reais que cada servidor dá ao tráfego que já está passando por ele — taxa de erros 5xx, timeouts, conexões recusadas. Um servidor que começa a falhar sistematicamente nas requisições reais é retirado do pool mesmo sem esperar o próximo ciclo de health check ativo, o que reduz o tempo de reação a um problema.
O ciclo completo é: falhou, sai; sarou, volta. Quando um servidor falha o health check (ou passa a errar demais), o load balancer o remove do pool ativo — deixa de mandar tráfego novo para ele — sem nenhuma intervenção manual. Quando o servidor volta a responder corretamente por um número consistente de verificações, ele é reincorporado ao pool automaticamente. É esse mecanismo, rodando continuamente e sem intervenção humana, que garante que "um servidor caiu" vire "o sistema perdeu um pouco de capacidade por um tempo" em vez de "o sistema caiu".
L4 vs. L7: balancear transporte ou aplicação#
Load balancers operam em camadas diferentes do modelo de rede, e a camada em que operam determina o que eles conseguem enxergar — e, por consequência, quão inteligentes podem ser nas decisões de roteamento.
Balanceamento de camada 4 (transporte). Um load balancer L4 opera sobre TCP/UDP: ele vê endereços IP e portas, mas não abre o conteúdo do pacote — não sabe se é uma requisição HTTP, o que ela pede, nem para qual caminho. A decisão de para qual servidor mandar é tomada olhando só para essas informações de baixo nível. A vantagem é a velocidade: sem precisar interpretar o conteúdo da aplicação, um L4 processa tráfego com latência mínima e overhead baixo, o que o torna adequado para volumes altíssimos ou protocolos que não são HTTP.
Balanceamento de camada 7 (aplicação). Um load balancer L7 entende o protocolo da aplicação — tipicamente HTTP/HTTPS — e enxerga o conteúdo da requisição: a URL, os cabeçalhos, os cookies, o método. Isso abre a porta para decisões de roteamento muito mais ricas: mandar requisições para /api/ para um pool de servidores de API e requisições para /img/ para um pool otimizado para servir arquivos estáticos, por exemplo, ou rotear com base num cabeçalho de versão para suportar múltiplas versões da API em paralelo. Um L7 também é o lugar natural para fazer terminação TLS — descriptografar o HTTPS uma vez, na borda, para que os servidores internos do pool troquem tráfego simples entre si, sem cada um precisar gerenciar certificados.
A troca entre os dois é a de sempre em sistemas: L4 é mais rápido e mais simples porque sabe menos; L7 é mais lento (comparativamente) e mais complexo porque entende mais — e é esse entendimento que permite roteamento inteligente por conteúdo da requisição, algo que um L4 estruturalmente não pode fazer.
O problema da sessão: onde mora o estado?#
Aqui mora uma das armadilhas conceituais mais comuns de sistemas balanceados. Se a lógica de negócio guarda alguma coisa sobre o usuário — os itens do carrinho, os dados da sessão de login — na memória de um servidor específico, então esse servidor é o único que sabe responder às próximas requisições daquele usuário. Se a próxima requisição cair em outro servidor do pool, esse outro servidor não tem a menor ideia de quem é o usuário nem do que ele estava fazendo.
A muleta mais comum é a sessão fixa (sticky session / session affinity). O load balancer é configurado para sempre mandar o mesmo cliente para o mesmo servidor — normalmente via IP Hash, ou via um cookie que o próprio load balancer injeta na primeira resposta e passa a usar para identificar aquele cliente daí em diante. Funciona, no sentido de que resolve o sintoma. Mas é uma muleta porque reintroduz, em miniatura, o problema que o load balancer existe para resolver: aquele cliente específico volta a depender de um servidor específico, o algoritmo de distribuição perde parte da sua liberdade de equilibrar carga, e se aquele servidor cai, todo mundo que estava "grudado" nele perde a sessão mesmo com o resto do pool saudável.
A solução estrutural é diferente: tirar o estado do servidor. Servidores stateless — sem memória local do usuário entre requisições — não têm esse problema porque não existe "o servidor certo" para nenhum cliente: qualquer servidor do pool está igualmente apto a atender qualquer requisição, porque tudo que ele precisa saber sobre o usuário vem de fora — de um token que a própria requisição carrega, ou de um armazenamento externo compartilhado, como Redis ou um banco de dados, que qualquer servidor do pool consulta. Com o estado externalizado, o load balancer recupera total liberdade para distribuir como quiser, sem afinidade nenhuma, e a perda de um servidor deixa de significar a perda de sessão de ninguém.
A recomendação de engenharia decorre direto disso: projetar a aplicação para ser stateless desde o início evita ter que recorrer a sessão fixa mais adiante — e evita a fragilidade que ela reintroduz.
O load balancer também pode ser ponto único de falha#
Há uma ironia possível aqui: se todo o tráfego passa por um único load balancer, e ele cai, o pool inteiro atrás dele fica inacessível mesmo que todos os servidores estejam saudáveis. Colocar um load balancer na frente resolve o problema de ponto único de falha nos servidores de aplicação — mas, sem cuidado, apenas move o ponto único de falha um nível acima.
A resposta é a mesma ideia aplicada recursivamente: não ter um load balancer só. Na prática isso é resolvido com um par (ou mais) de load balancers redundantes, com algum mecanismo — um IP flutuante que migra automaticamente para o balancer saudável, DNS com múltiplos endereços, ou roteamento anycast que direciona o cliente para a instância mais próxima e disponível — garantindo que a saída de um load balancer específico não tire o sistema do ar. Em provedores de nuvem, essa redundância costuma vir embutida na oferta de load balancer gerenciado, mas o princípio vale independentemente de quem opera: qualquer componente que fica "na frente de tudo" precisa ele mesmo não ser um ponto único.
Onde o load balancer aparece na prática#
Sem entrar em comparação de produtos, vale situar as formas mais comuns em que esse papel aparece numa arquitetura real. Pode ser um software dedicado rodando como sua própria camada — as ferramentas mais conhecidas nesse espaço lidam tanto com balanceamento quanto com proxy reverso e terminação TLS. Pode ser um serviço gerenciado de nuvem, onde o provedor cuida da redundância, do escalonamento e da manutenção do próprio balancer, expondo só a configuração de regras e health checks. E, cada vez mais, pode aparecer embutido — dentro de um API gateway que já faz outras funções de borda, ou como parte de um service mesh, onde o balanceamento acontece de forma descentralizada, ao lado de cada serviço, em vez de num ponto central único.
Vale notar a sobreposição de papéis com proxy reverso e API gateway, porque a linha entre eles é frequentemente borrada na prática. Um proxy reverso encaminha requisições para servidores por trás dele — e um load balancer é, estruturalmente, um tipo de proxy reverso que faz essa escolha entre múltiplos destinos equivalentes. Um API gateway vai além: além de rotear (às vezes fazendo o próprio balanceamento), ele lida com autenticação, limitação de taxa, transformação de requisição e outras preocupações de borda. Na prática, é comum que uma única peça de infraestrutura acumule os três papéis ao mesmo tempo — e não há problema conceitual nisso, desde que fique claro qual responsabilidade está sendo exercida em cada configuração.
Fechando#
O load balancer é o que torna a frase "só adicione mais servidores" verdadeira na prática, e não apenas uma promessa vaga de escalabilidade. Sem ele, escalar horizontalmente não tem como funcionar — não há como um cliente saber para qual das várias máquinas mandar seu pedido —, e o sistema fica preso ao teto (e à fragilidade) de escalar uma máquina só cada vez mais para cima. Com ele, adicionar capacidade vira uma operação incremental e transparente: mais um servidor no pool, e o load balancer passa a incluí-lo na distribuição.
O que faz esse mecanismo confiável não é o algoritmo de distribuição isolado — é a combinação dele com health checks que removem e devolvem servidores automaticamente, com a escolha correta de camada (L4 para velocidade bruta, L7 para roteamento inteligente por conteúdo), e com uma arquitetura de aplicação que não depende de estado preso a um servidor específico. Ignorar essa última parte é a forma mais comum de anular, na prática, os benefícios que o load balancer deveria trazer — reintroduzindo, via sessão fixa, exatamente a dependência de máquina única que todo o design existia para eliminar. Entender essas peças juntas é o que separa "colocamos um load balancer na frente" de "o sistema de fato não tem mais um ponto único de falha".