English· Español· Deutsch· Nederlands· Français· 日本語· ქართული· 繁體中文· 简体中文· Português· Русский· العربية· हिन्दी· Italiano· 한국어· Polski· Svenska· Türkçe· Українська· Tiếng Việt· Bahasa Indonesia

un

visitante
1 / ?

Duas Formas de Suportar Mais Carga

Bem-vindo

Quando um serviço começa a ceder sob carga, o operador enfrenta uma escolha. Tornar a máquina existente maior (mais CPU, mais RAM, discos mais rápidos). Ou adicionar mais máquinas que cada uma faz o mesmo trabalho.

O primeiro caminho é chamado de escala vertical (scale up). O segundo é chamado de escala horizontal (scale out).

Esta lição ensina por que quase toda arquitetura web moderna escolhe a escala horizontal e qual propriedade da carga de trabalho torna essa escolha viável. A resposta está escondida em uma única palavra: estado.

Ao final, você entenderá:

- As curvas de custo da escala vertical vs. horizontal & onde cada uma faz sentido

- O que 'stateful' & 'stateless' significam na prática, & por que um deles se multiplica de forma barata

- A matemática que dimensiona uma frota de réplicas sob carga esperada & de pico

- A regra de folga que impede uma camada de colapsar além do joelho da fila

- Onde o estado deve residir (ele nunca desaparece) & como removê-lo das camadas que precisam escalar

Por que a Escala Horizontal Vence Além de um Limite

Escala Vertical: Uma Caixa Maior

Vantagens: simples. Sem alterações de código. Sem coordenação. O mesmo processo agora tem mais CPU.

Desvantagens: teto. A VM comercialmente disponível mais tem RAM e núcleos finitos. Acima disso, nenhum dinheiro compra mais folga. Os custos aumentam de forma superlinear além do ponto ideal da oferta de um fornecedor. Uma falha naquela única máquina derruba o serviço inteiro.

Escalonamento Horizontal: Muitas Caixas Menores

Vantagens: sem teto (até onde sua disposição para pagar e coordenar máquinas permitir). A capacidade aumenta linearmente com o número de réplicas, de forma previsível. Uma falha em uma única réplica remove 1/N da capacidade, não 100%.

Desvantagens: requer que a carga de trabalho suporte isso. Algumas cargas de trabalho (um único banco de dados grande, um servidor de jogo com estado mantendo sessões ativas) resistem ao escalonamento horizontal. A coordenação e a distribuição de carga tornam-se preocupações operacionais.

O ponto de cruzamento: qualquer serviço de produção que precise sobreviver a qualquer falha de uma única máquina deve ser executado em pelo menos duas máquinas. Uma vez que você aceita duas, já escolheu o escalonamento horizontal. A partir daí, a pergunta não é "devemos?" mas "com que economia podemos adicionar a próxima réplica?"

O principal habilitador: uma carga de trabalho que não mantém estado por requisição na própria máquina. Então, qualquer réplica pode responder a qualquer requisição, e adicionar uma réplica adiciona capacidade sem coordenação.

Escalonamento vertical vs horizontal: curva de custo e teto

Uma equipe executa um serviço em uma única VM com 8 CPUs, processando 800 requisições por segundo com 60% de utilização de CPU. Espera-se que o tráfego cresça 4 vezes no próximo ano. Eles estão debatendo: alugar uma VM de 32 CPUs (escala vertical) ou executar quatro VMs idênticas de 8 CPUs atrás de um balanceador de carga (escala horizontal). Qual opção você recomenda e cite dois motivos que vão além da capacidade bruta.

Com Estado vs Sem Estado na Prática

O Estado Nunca Desaparece, Ele Apenas se Move

Componente com estado: retém informações cuja perda alteraria o comportamento. Um banco de dados que armazena contas de usuários. Um cache que armazena tokens de sessão. Um worker que fixa uma conexão de streaming de longa duração a um usuário específico.

Componente sem estado: não retém nenhuma informação cuja perda seria relevante. Uma camada web que lê uma requisição, consulta um banco de dados e escreve uma resposta. Cada requisição é independente; a camada não retém nada entre as requisições.

Insight principal: o estado nunca desaparece de um sistema. Ele se move para uma camada projetada para retê-lo (um banco de dados, um cluster Redis, um armazenamento de objetos). As camadas que enfrentam o tráfego podem então ficar sem estado, e as camadas sem estado escalam horizontalmente porque qualquer réplica pode responder a qualquer requisição.

Teste prático: se você matasse aleatoriamente um processo nesta camada e o reiniciasse, algum usuário experimentaria uma resposta incorreta ou perderia a sessão? Se sim, ele retém estado. Se não, ele não retém.

Exemplos

- Um processo web em Python que lê requisições, consulta o Postgres e retorna JSON: sem estado. O estado reside no Postgres.

- Um processo web em Python que mantém carrinhos de compras de usuários na memória local: com estado. Matar o processo faz perder os carrinhos.

- Um servidor WebSocket que mantém conexões abertas com usuários do chat: com estado no sentido de conexão. Encerrar o processo derruba as conexões; os clientes devem se reconectar. Frequentemente, esses sistemas ainda podem escalar horizontalmente com cuidado (sessões fixas, hash consistente).

- Um cache Redis à frente do Postgres: com estado para o conteúdo do cache, mas aceitável se falhas de cache (cache misses) forem toleráveis. Uma falha em uma réplica significa falha de cache, não perda de dados.

Projetar para escalabilidade horizontal = remover o estado da camada que precisa escalar.

Auditar uma Camada Suspeita

Uma equipe executa uma API de recomendações em 6 VMs de backend atrás de um proxy reverso. A aplicação: lê um ID de usuário da requisição, busca a atividade recente do usuário no Postgres, executa um algoritmo de pontuação e retorna uma lista de itens recomendados. Dois comportamentos não padrão:

- A aplicação mantém um cache de 'atividade recente do usuário' na memória do processo, populado na primeira requisição para um usuário e reutilizado em requisições subsequentes.

- A aplicação usa sessões fixas (sticky sessions): uma vez que um usuário acessa a VM #3, todas as suas requisições subsequentes vão para a VM #3 (o proxy é configurado para roteamento fixo baseado em cookie).

Identifique qual desses dois comportamentos torna a camada com estado e explique o que quebraria se a equipe tentasse escalar de 6 VMs para 12. Em seguida, proponha um redesenho que permita à camada escalar horizontalmente sem perder o benefício do cache de usuário.

A Fórmula de Réplicas

A Fórmula de Capacidade Mais Simples

Uma vez que uma camada se torna sem estado, dimensioná-la torna-se uma questão de aritmética. Você precisa de réplicas suficientes para que a carga em estado estável chegue e saia na mesma taxa, com folga para picos.

A fórmula:

réplicas = ⌈ (carga_pico × fator_de_pico) / capacidade_por_réplica ⌉ + folga

Onde:

- carga_pico: o máximo de requisições por segundo sustentado que você espera na operação normal

- fator_de_pico: um multiplicador que cobre rajadas breves acima do pico (comumente de 1,5x a 2x para tráfego previsível, 3x ou mais para viral / imprevisível)

- capacidade_por_réplica: requisições por segundo que uma réplica processa com latência e utilização aceitáveis (comumente medido em 70% de CPU, não na saturação)

- folga: réplicas extras para que algumas falhas de réplicas não colapsem a camada (comumente 1-2 réplicas para frotas pequenas, 10-20% para as maiores)

Exemplo resolvido: um backend processa 100 req/s com 70% de CPU por réplica. A carga de pico é de 600 req/s. Você espera picos ocasionais de 2x. Deseja suportar a falha de 2 réplicas.

réplicas = ⌈ (600 × 2) / 100 ⌉ + 2 = 12 + 2 = 14 réplicas

A Regra dos 80%

A capacidade por réplica não é o ponto de saturação. Meça a capacidade com 70-80% de CPU, nunca em 100%.

Acima de aproximadamente 80% de utilização, as curvas de enfileiramento sobem abruptamente: uma fila que operava em 10 ms com 60% de utilização passa a operar em 80 ms com 90% de utilização. A latência, e não a taxa de transferência, é o que se degrada primeiro. (A lição complementar geometry_of_stateless_horizontal_scaling deriva essa curva matematicamente.)

Autoescalabilidade vs. Provisão Estática

Estática: provisione para o pico × folga de picos e aceite o custo de operar com baixa utilização fora do pico.

Autoescalabilidade: um controlador adiciona e remove réplicas com base na utilização observada, na latência alvo ou na profundidade da fila.

Aviso sobre autoscaling: o tempo de inicialização a frio é importante. Se um novo réplica levar 2 minutos para iniciar, o autoscaling não poderá responder a um pico de 30 segundos. Um autoscaling maduro mantém um pool quente de réplicas pré-provisionadas logo abaixo do limiar de escala para cima.

Fórmula de dimensionamento de réplicas com exemplo resolvido

Dimensione uma Frota para um Novo Serviço

Seu equipe planeja lançar uma API de metadados de vídeo. Os benchmarks mostram que uma única réplica processa 250 req/s com 70% de CPU e latência p99 de 50 ms. O marketing prevê uma carga de pico de 4.000 req/s durante os horários de pico. Um evento promocional planejado pode causar um surto de 3x o pico brevemente. Você deseja que o serviço sobreviva a 3 falhas simultâneas de réplicas sem exceder 80% de utilização nas réplicas restantes.

Aplique a fórmula de réplicas para dimensionar a frota de lançamento. Mostre seu trabalho passo a passo e, em seguida, explique um motivo pelo qual seu número ainda pode estar errado (inicialização a frio, aquecimento de cache, latência de dependência, qualquer coisa que você possa defender).

Início Frio, Drenagem Lenta & Outras Bordas Reais

Frotas Reais Têm Bordas Reais

A fórmula assume que os réplicas aparecem instantaneamente, aceitam tráfego instantaneamente & descartam tráfego instantaneamente. Nenhuma dessas premissas se sustenta em produção.

Início frio (Cold start): um novo réplica precisa iniciar o SO, iniciar o processo, carregar a configuração, aquecer os caches & passar nas verificações de saúde. Isso pode levar de 5 segundos (reinício de contêiner) a 5 minutos (inicialização completa da VM + download da imagem). O dimensionamento automático não pode responder a picos mais curtos que esse atraso.

Drenagem lenta (Slow drain): um réplica sendo removido do pool precisa de tempo para finalizar as solicitações em andamento antes de ser encerrado. Caso contrário, os usuários veem respostas truncadas. Os proxies reversos suportam drenagem (parar de aceitar novas solicitações, finalizar as ativas), mas isso leva de segundos a minutos.

Pool quente: frotas de produção mantêm um pool de réplicas pré-provisionadas, porém ociosas, prontas para assumir tráfego sob sinal. Troca um pequeno custo constante por uma resposta rápida a picos de demanda.

Drenagem de conexões vs. encerramento imediato: o encerramento gracioso é importante. Um SIGTERM que dispara a drenagem leva mais tempo que um SIGKILL, mas não interrompe as requisições dos usuários.

Janela de verificação de saúde: uma réplica que acabou de iniciar pode passar na primeira verificação de saúde antes que o pool de conexões do banco de dados esteja "quente"; o proxy então envia tráfego real e as primeiras dúzias de requisições ficam lentas. Ajuste as verificações de saúde para testar o caminho real, e não apenas a liveness do processo.

Crescimento de aderência (stickiness): mesmo camadas nominalmente sem estado adquirem aderência ao longo do tempo (caches de CDN, caches de resolvers DNS, pools de conexões). Desconfie de "réplicas idênticas" que, ainda assim, se comportam de forma diferente.

Pool Quente ou Autoscaling Reativo?

Sua API de metadados de vídeo (a mesma da pergunta anterior, dimensionada com 51 réplicas para o pico estável + surto) experimenta um surto de 30 segundos para 5x a carga normal sempre que um novo vídeo viral é enviado. O autoscaling atualmente leva 90 segundos para adicionar uma nova réplica a partir do zero (pull da imagem + aquecimento). Durante essa lacuna de 90 segundos, a latência sobe acentuadamente & algumas requisições expiram (timeout).

Proponha uma solução. Escolha: (a) manter o autoscaling reativo, mas ajustá-lo de forma diferente, (b) provisionar um pool quente de réplicas ociosas, ou (c) provisionar estaticamente para o pico de 5x permanentemente. Justifique sua escolha & nomeie um custo que as outras duas opções imporiam.

Projetar uma Camada Stateless Sob Restrições

Síntese

Você aprendeu por que a escala horizontal vence além de um pequeno limiar, o que significa estado na prática, como dimensionar uma frota sob carga esperada & de pico, & onde a escala horizontal falha nas bordas.

Aplique os quatro.

Projete uma camada de backend para feed.example.com, uma API de feed social. Restrições: capacidade por réplica de 200 req/s a 70% de CPU; carga de pico esperada de 1500 req/s; fator de surto de 2,5x (histórias em alta ocasionais); sobreviver a 2 falhas simultâneas de réplicas; tempo de inicialização a frio de 60 segundos; os surtos podem durar 45 segundos; o orçamento permite alguma capacidade ociosa, mas não a provisionamento permanente de 2,5x.

Dimensione a frota estável (mostre o cálculo), escolha sua estratégia de tratamento de surtos (pool quente / autoscaling reativo / ambos) e identifique um item de estado por usuário que o aplicativo provavelmente mantém e para onde ele deve ser movido para manter a camada sem estado.

Para Onde Este Curso Vai a Seguir

Para Onde Este Curso Vai a Seguir

Você agora possui um modelo mental funcional de uma camada sem estado: por que ela escala, como dimensioná-la, o que quebra em suas bordas e para onde o estado deve se mover quando você a empurra além da camada que precisa crescer.

A próxima lição deste curso (cs_distsys_ingress_egress_separation) aborda um problema mais sutil: mesmo uma camada sem estado perfeitamente dimensionada pode falhar de maneiras surpreendentes quando o tráfego de entrada e de saída compartilham o mesmo caminho de rede. O exemplo clássico envolve um proxy tentando se conectar a si mesmo; a correção envolve dividir uma camada em duas com responsabilidades diferentes.

Lição complementar: geometry_of_stateless_horizontal_scaling deriva a curva de enfileiramento, a Lei de Little aplicada a uma frota de réplicas e o significado geométrico do joelho de 80% de utilização.

Muito bem. Em frente.