Balanceamento de carga#

O Angie distribui as requisições de clientes entre um grupo de servidores upstream descrito por um bloco upstream. Qual servidor atende a uma determinada requisição é decidido pelo método de balanceamento do grupo.

Este guia aborda o balanceamento HTTP, configurado no módulo HTTP Upstream. O balanceamento de sessões TCP e UDP funciona da mesma forma e é configurado no módulo Stream Upstream; as diferenças são indicadas onde forem relevantes.

Um grupo usa exatamente um método de balanceamento. Se um bloco especificar mais de um, o último prevalece e o Angie registra load balancing method redefined; um bloco que não especificar nenhum usa Round-robin ponderado.

Escolhendo um método#

O método determina como o Angie escolhe um servidor para cada requisição, e cada método aceita seu próprio conjunto de parâmetros de server. Um parâmetro que o método não aceita não é ignorado: o Angie se recusa a iniciar e informa balancing method does not support parameter. Consulte esta tabela antes de combinar um método com weight, slow_start ou backup.

Método

Distribui as requisições por

Nível

weight

slow_start

backup

Round-robin ponderado

Posição em uma rotação ponderada. O padrão; sem diretiva.

L4, L7

hash

Hash de uma chave escolhida por você, como um URI ou um cabeçalho.

L4, L7

ip_hash

Hash do endereço do cliente.

L7

least_conn

Menor número de conexões ativas por unidade de peso.

L4, L7

random

Chance proporcional ao peso.

L4, L7

least_time

Chance inversamente proporcional ao tempo médio de resposta.

L4, L7

feedback (PRO)

Chance proporcional a um valor informado pelos próprios servidores.

L4, L7

Todos os métodos acima estão disponíveis no Angie, exceto feedback, que requer o Angie PRO. ip_hash não tem equivalente no Stream; use hash com $remote_addr em seu lugar.

Métodos de balanceamento#

Round-robin ponderado#

O round-robin é o método padrão: o Angie o usa sempre que um grupo não especifica outro método, e ele não tem diretiva própria. O Angie percorre os servidores do grupo em rotação, dando a cada um uma parcela da rotação proporcional ao seu weight:

upstream backend {
    zone backend 1m;
    server backend1.example.com weight=5;
    server backend2.example.com;
    server backend3.example.com;
}

A cada sete requisições, cinco vão para backend1.example.com e uma para cada um dos outros dois. A distribuição é uniforme: as requisições para o servidor de maior peso são espalhadas ao longo da rotação, em vez de enviadas em uma única rajada, de modo que o grupo nunca recebe cinco requisições consecutivas para um servidor seguidas de duas para os demais.

O round-robin também serve como alternativa para outros métodos. Quando hash, ip_hash ou random não conseguem decidir por um servidor utilizável, eles delegam a escolha ao round-robin.

hash#

O método hash envia ao mesmo servidor todas as requisições cujas chaves têm o mesmo hash. Isso ajuda quando os próprios servidores do grupo armazenam respostas em cache, como proxies de cache ou nós memcached. Enquanto a composição do grupo não muda, as requisições para um determinado URI sempre chegam ao mesmo servidor, de modo que a resposta fica em cache nesse único servidor, e não em todos os servidores do grupo.

upstream backend {
    zone backend 1m;
    hash $request_uri consistent;
    server backend1.example.com;
    server backend2.example.com;
}

A chave é qualquer string de variáveis. Os pesos funcionam dando a um servidor mais pesado proporcionalmente mais espaço de chaves.

Sem consistent, o Angie mapeia o hash da chave em um intervalo do tamanho do peso total do grupo, em que cada servidor ocupa um segmento do tamanho do seu peso. Adicionar ou remover um servidor altera esse total e, portanto, remapeia muitas chaves além das atribuídas ao servidor adicionado ou removido: as respostas delas em cache permanecem em servidores que deixaram de receber requisições para elas. Com consistent, o Angie posiciona os servidores em um anel ketama e, ao remover um servidor, remapeia apenas as chaves que esse servidor guardava.

Nota

As posições de um servidor no anel são calculadas a partir do nome como escrito na diretiva server, e não dos endereços para os quais o nome é resolvido. Todos os endereços de um mesmo nome compartilham essas posições, de modo que o nome ocupa no anel o mesmo espaço que um único servidor de mesmo peso. Quando uma chave cai em uma das posições do nome, o Angie escolhe um de seus endereços por round-robin.

Sem consistent, cada endereço conta como um servidor separado, com peso próprio, e o hash é mapeado no peso total de todos os endereços. Um nome que resolve para vários endereços recebe, portanto, uma parcela proporcional ao número desses endereços.

ip_hash#

O método ip_hash usa como chave o endereço do cliente: os três primeiros octetos de um endereço IPv4, ou o endereço IPv6 completo. As requisições de um mesmo cliente chegam a um único servidor enquanto esse servidor estiver disponível.

Ele não tem modo de hash consistente, portanto alterar o conjunto de servidores pode redirecionar muitos clientes para outros servidores. Para tirar um servidor de serviço preservando o restante do mapeamento, marque-o com down em vez de apagar a sua linha:

upstream backend {
    zone backend 1m;
    ip_hash;
    server backend1.example.com;
    server backend2.example.com down;
    server backend3.example.com;
}

least_conn#

O método least_conn escolhe o servidor com o menor número de conexões ativas por unidade de peso — ele minimiza a razão entre conexões e peso, não o número bruto. Por isso, um servidor com weight=2 mantendo 15 conexões é preferido a um com weight=1 mantendo 10. Quando vários servidores empatam, eles são tentados em sequência por round-robin ponderado.

Só contam as conexões que atualmente carregam uma requisição. As conexões keepalive ociosas não contam.

Use-o quando as requisições diferem muito no tempo que ocupam um servidor: downloads grandes, long polling, WebSocket. O round-robin envia a cada servidor o mesmo número de requisições por unidade de peso, mas esse número diz pouco sobre a carga quando algumas requisições ocupam um servidor por milissegundos e outras por minutos; o least_conn conta as requisições que o servidor está atendendo neste momento. Se, em vez disso, os servidores diferem em velocidade, use least_time.

random#

O método random escolhe um servidor aleatoriamente, com probabilidade proporcional ao peso. Ao longo de muitas requisições, a distribuição corresponde aos pesos.

O parâmetro two escolhe dois servidores aleatoriamente e então utiliza aquele, entre os dois, com menos conexões ativas por unidade de peso:

upstream backend {
    zone backend 1m;
    random two;
    server backend1.example.com;
    server backend2.example.com;
    server backend3.example.com;
}

Isso importa quando vários balanceadores atendem aos mesmos servidores sem compartilhar estado. Se cada balanceador escolher isoladamente o servidor menos carregado, todos convergem para o mesmo alvo; escolher o melhor entre dois candidatos aleatórios distribui a carga em vez disso. Com um único balanceador, least_conn é a escolha mais simples.

Nota

random two least_conn é aceito por compatibilidade e se comporta exatamente como random two; comparar conexões ativas é o único modo que two possui.

least_time#

O método least_time favorece os servidores que respondem rapidamente. Seu modo define o que é medido: header é o tempo para receber o cabeçalho da resposta, last_byte, o tempo para receber a resposta inteira.

Um grupo cujos servidores são comparados pelo tempo até o cabeçalho da resposta:

upstream backend {
    zone backend 1m;
    least_time header;
    server backend1.example.com;
    server backend2.example.com;
}

A medição é uma média móvel exponencialmente ponderada, de modo que uma única resposta lenta não desloca muito a posição de um servidor. A diretiva response_time_factor, ou o parâmetro factor, define o peso que o histórico acumulado mantém diante de cada nova medição: um fator alto reage lentamente, um fator baixo esquece rapidamente.

Vale conhecer duas propriedades antes de escolher esse método.

Ele não escolhe simplesmente o servidor mais rápido. A chance de um servidor receber a requisição é inversamente proporcional ao seu tempo médio de resposta. Mesmo um servidor consistentemente lento mantém uma pequena parcela — cerca de um por cento do que um servidor médio recebe, ou seja, cerca de uma requisição em cada 100 x N para um grupo de N servidores. Isso é deliberado: um servidor que não recebesse nada nunca produziria uma nova medição, e nunca se poderia constatar que ele se recuperou.

Ele rejeita weight. Um grupo que combina least_time com weight= em qualquer servidor não inicia. O tempo de resposta já reflete quanta carga um servidor está absorvendo, portanto não resta nada para um peso expressar.

No Stream, least_time aceita um conjunto diferente de modos: connect (o tempo para estabelecer a conexão com o servidor), first_byte (o tempo para receber o primeiro byte de dados do servidor) e last_byte (o tempo até o fim da sessão); header não está disponível.

feedback#

O método feedback (PRO) permite que os próprios servidores informem quanto trabalho desejam receber. O Angie lê um valor em cada resposta — normalmente um cabeçalho — e concede ao servidor uma parcela de requisições proporcional a esse valor, calculada em média da mesma forma que least_time calcula a média do tempo de resposta.

upstream backend {
    zone backend 1m;
    feedback $upstream_http_x_capacity;
    server backend1.example.com;
    server backend2.example.com;
}

Aviso

O valor é lido na direção esperada para capacidade disponível: um valor informado mais alto garante uma parcela maior. Se sua métrica sobe conforme o servidor fica mais ocupado — carga de CPU, profundidade de fila — adicione o parâmetro inverse, ou você enviará mais tráfego para o servidor mais sobrecarregado.

O parâmetro account seleciona quais respostas alimentam a média, o que permite basear o balanceamento nas respostas de upstream_probe (PRO) em vez do tráfego de clientes.

Pesos dos servidores e parcelas de tráfego#

weight não significa a mesma coisa para todos os métodos, e apenas em alguns deles a parcela de tráfego de um servidor é igual ao seu peso dividido pela soma de todos os pesos:

Método

O que weight faz

round-robin, random

Define a parcela diretamente. A parcela é igual ao peso dividido pelo peso total.

hash, ip_hash

Define a parcela do espaço de chaves. O tráfego só corresponde aos pesos se as próprias chaves estiverem distribuídas uniformemente; endereços de clientes reais e URIs geralmente não estão.

least_conn

Divide a contagem de conexões que está sendo comparada. Um servidor mais pesado é levado a um número proporcionalmente maior de conexões simultâneas, não a um número proporcionalmente maior de requisições.

least_time

Nada — o parâmetro é rejeitado na inicialização.

feedback

Multiplica o valor informado pelo servidor. O peso é um dos dois fatores, portanto a parcela não é previsível a partir do peso isoladamente.

Assim que max_conns, slow_start, max_fails, down ou servidores de backup entram em jogo, nenhum método distribui o tráfego estritamente por peso.

Falhas e recuperação#

Verificações passivas de integridade#

O Angie avalia um servidor pelo resultado das requisições enviadas a ele. Se uma tentativa satisfaz uma condição de nova tentativa e o limite de tentativas não se esgotou, o Angie pode repassar a requisição a outro servidor. A diretiva proxy_next_upstream e suas equivalentes definem essas condições; proxy_next_upstream_tries e proxy_next_upstream_timeout limitam o número e a duração das tentativas.

max_fails tentativas malsucedidas dentro de fail_timeout marcam um servidor como indisponível pelo mesmo período, após o qual ele é testado novamente. Os padrões são uma tentativa e dez segundos. O que conta como tentativa malsucedida é definido por proxy_next_upstream e seus equivalentes.

Definir max_fails=0 desabilita a verificação para esse servidor: ele nunca é marcado como indisponível.

Nota

Abaixo do limite de max_fails, uma falha individual ainda reduz a parcela do servidor, que se recupera gradualmente. Sem slow_start, o Angie reduz o peso efetivo usado pelo round-robin e por least_conn e depois o restaura nas requisições seguintes. Com slow_start, cada falha faz o servidor recuar na rampa baseada em tempo usada por todos os métodos que aceitam o parâmetro. Ambos os comportamentos exigem max_fails maior que um: com o valor padrão de um, a primeira falha marca o servidor como indisponível, portanto não há estado intermediário.

Verificações ativas de integridade#

As verificações passivas só sabem de um servidor pelas requisições dos clientes: uma falha é detectada depois que requisições de clientes a esse servidor já falharam, e uma recuperação, só quando uma requisição de cliente é enviada a ele de novo. As verificações ativas, disponíveis no Angie PRO, contatam os servidores em intervalos regulares, independentemente do tráfego dos clientes, e percebem tanto a falha quanto a recuperação sem gastar requisições de clientes com nenhuma das duas.

Uma verificação é configurada pela diretiva upstream_probe (PRO) no mesmo bloco location que repassa as requisições ao grupo; o próprio grupo precisa de uma zona de memória compartilhada:

upstream backend {
    zone backend 1m;
    server backend1.example.com;
    server backend2.example.com;
}

map $upstream_status $good {
    200     "1";
}

server {
    listen 80;

    location / {
        proxy_pass http://backend;
        upstream_probe backend_probe uri=/health interval=5s
                       test=$good fails=3 passes=2;
    }
}

A cada cinco segundos, o Angie requisita /health a cada servidor do grupo. Três verificações malsucedidas seguidas marcam o servidor como não íntegro; duas bem-sucedidas seguidas o devolvem ao serviço. Sem o parâmetro test, uma verificação falha apenas quando uma requisição de cliente neste location contaria como tentativa malsucedida. Por padrão, proxy_next_upstream conta apenas um erro ou um timeout, de modo que uma resposta 500 ou 503 não reprova a verificação. Aqui, test=$good aprova a verificação somente se o servidor responder 200: para qualquer outro status, map deixa a variável vazia.

As respostas das verificações também podem alimentar o método feedback por meio do seu parâmetro account.

Um servidor considerado não íntegro é excluído da escolha por todos os métodos de balanceamento. Após um reload da configuração, as verificações recomeçam. Por padrão, o servidor continua recebendo requisições sem esperar pela primeira verificação após o reload. Se upstream_probe tiver o parâmetro essential, o servidor não recebe requisições até que a verificação o encontre íntegro pela primeira vez (neste exemplo, após duas verificações bem-sucedidas seguidas); adicionando persistent, que só funciona junto com essential, essa espera deixa de valer para os servidores que estavam íntegros antes do reload, porque o seu indicador de integridade é mantido na zona (consulte Estado compartilhado entre processos worker).

Servidores de backup#

Servidores marcados com backup ficam em reserva:

upstream backend {
    zone backend 1m;
    server backend1.example.com;
    server backend2.example.com;

    server backup1.example.com backup;
}

Toda requisição começa pelos servidores primários e só chega ao grupo de backup quando nenhum servidor primário pode atendê-la. Não existe um modo de failover duradouro do qual seja preciso retornar — assim que um servidor primário volta a passar em sua verificação de integridade, a próxima requisição já o utiliza.

O Angie PRO pode inverter esse comportamento com backup_switch (PRO) permanent, que continua enviando requisições ao grupo que as atendeu por último. Sem um argumento de tempo, a alternância nunca reverte por conta própria; com permanent=tempo, o Angie envia uma requisição de volta ao grupo primário após esse intervalo para verificar se ele se recuperou.

O parâmetro backup é rejeitado por hash, ip_hash e random.

Devolvendo um servidor ao serviço#

O parâmetro slow_start eleva gradualmente um servidor recuperado até seu peso total ao longo de um período, em vez de restaurá-lo de uma só vez, o que evita que um cache frio ou um processo recém-iniciado seja atingido em taxa máxima:

server backend1.example.com slow_start=30s;

Na maioria das vezes, ele se aplica depois que um servidor foi marcado como indisponível e se recuperou; uma falha individual também faz o servidor recuar na mesma rampa (consulte Falhas e recuperação). Ele funciona apenas com os métodos marcados na coluna slow_start em Escolhendo um método. Diferentemente de algumas outras implementações, ele não exige a diretiva zone.

Para tirar um servidor da rotação deliberadamente, use down; para parar de enviar-lhe novo tráfego enquanto deixa as sessões vinculadas terminarem, use drain.

Estado compartilhado entre processos worker#

O balanceamento só é coerente em toda uma instância do Angie se os workers compartilharem sua visão do grupo. A diretiva zone fornece essa visão compartilhada:

upstream backend {
    zone backend 1m;
    server backend1.example.com;
    server backend2.example.com;
}

Sem ela, cada processo worker mantém sua própria cópia do estado de execução do grupo — contadores de falhas, número de conexões ativas, a posição na rotação round-robin, médias de tempo de resposta — e balanceia de forma independente dos demais. Com N workers, você efetivamente executa N balanceadores sobre os mesmos servidores: um worker pode considerar um servidor indisponível enquanto os demais continuam enviando-lhe requisições, e um limite max_conns se aplica por worker, permitindo até N vezes mais conexões do que o configurado.

É também a zona que torna o grupo visível para as métricas de upstream, e ela é exigida pelos parâmetros resolve e service da diretiva server.

Nota

Um reload da configuração cria a zona novamente, com ou sem alteração de size: contadores, estado das verificações passivas e o histórico acumulado de balanceamento recomeçam do zero. Duas coisas são mantidas: os endereços resolvidos para servidores com resolve e o indicador de integridade em que se baseiam as verificações ativas com o parâmetro persistent (consulte Verificações ativas de integridade).

Afinidade de sessão e controles relacionados#

O balanceamento decide para onde vai uma requisição. Duas diretivas limitam essa decisão:

sticky

Vincula a sessão de um cliente ao servidor que a atendeu primeiro, identificada por um cookie, um valor de rota ou — com learn — um valor que o Angie observa no tráfego. A vinculação de sessão sobrepõe o método de balanceamento enquanto a sessão durar.

queue (PRO)

Retém brevemente uma requisição quando nenhum servidor pode atendê-la, em vez de falhá-la imediatamente. Útil quando max_conns é atingido durante picos curtos de carga.

A diretiva keepalive não afeta qual servidor é escolhido: ela mantém abertas conexões ociosas com os servidores, para que uma requisição a um servidor já escolhido reutilize uma delas em vez de abrir uma nova. O número dessas conexões é limitado por processo worker, e não no total.

Aviso

As diretivas sticky, keepalive e queue devem aparecer depois da diretiva que define o método de balanceamento; caso contrário, elas não têm efeito.