Startups: conheçam os desafios na hora de escalar seu app
Escalabilidade é frequentemente um problema para startups em crescimento, principalmente quando feita tarde. Alguns desenvolvedores experientes e até arquitetos em formação só se dão conta da necessidade de escalar sua aplicação quando o cliente bate na porta da equipe de suporte dizendo que o app está dando erro 5xx.
A ideia desse artigo é brevemente pontuar algumas estratégias e desafios que encontrei ao escalar minhas aplicações.
O problema…
Sua startup decola. Usuários estão aderindo ao seu serviço ou plataforma diariamente. Seu modesto servidor hospedado naquele plano de 29,90/mês está surpreendentemente dando conta do recado. Um belo dia, o consumo de CPU vai pras alturas e clientes começam a ligar pro seu telefone dizendo que o website não mais funciona. O caos se instaura e você passa as próximas 2 horas com a empresa de hospedagem tentando fazer o upgrade do seu plano. Problema resolvido, certo? É um pouco mais complicado que isso.
Escalabilidade vertical e horizontal
O que você fez foi escalar sua aplicação de forma vertical, também conhecido como Scaling Up. É o ato de aprimorar os recursos do seu servidor, como RAM, processador, interface de rede, etc. Não há nada de errado com isso e funciona muito bem em alguns cenários, porém após certo ponto se torna caro demais e existe um número finito de conexões que seu servidor pode abrir. Pra isso, existe uma solução melhor: escalabilidade horizontal, ou Scaling Out.
Na escalabilidade horizontal mais servidores são adicionados para rodar sua aplicação. Esse processo é também conhecido como clustering, visto que os servidores trabalham de forma transparente para o cliente e em conjunto, como nós num cluster.
Escalabilidade != Alta Disponibilidade
Alta disponibilidade significa que o cliente possui outros nós para receber sua requisição caso um dos nós falhe. Possuir alta disponibilidade não significa que a aplicação é escalável. Escalar uma aplicação significa poder adicionar mais recursos (de forma horizontal ou vertical) a medida que esses se fazem necessários.
Pense no seguinte cenário: sua startup foi citada em uma das maiores conferências de tecnologia da região e do dia pra noite centenas de usuários começam a se cadastrar e usar de uma só vez. Escalabilidade significa adicionar (preferencialmente de forma automática) mais recursos para sua aplicação pra aguentar a carga de novos usuários.
Load Balancer
Existe muito a se falar sobre load balancers, mas vamos simplificar: o load balancer é um mecanismo que cuida de receber o tráfego do cliente e distribuir para os nodes do seu cluster. Dessa forma, o acesso do cliente a sua aplicação ocorre de forma transparente.

Simplificando a vida com Containers
Containers ajudam a automatizar o processo de adicionar mais nodes ao cluster. É necessário garantir que todos os servidores do cluster possuam as mesmas características para operarem de forma igual. Um cluster não pode estar rodando uma versão do JVM enquanto outra está rodando uma versão anterior.
O sistema de containers é uma maneira de criar um ambiente exatamente da forma que você descreveu. Isto é, instalar o SO, dependências, softwares, servidores e aplicações de forma automatizada. Assim, toda vez que um node (container) for adicionado ao cluster ele vai ser exatamente igual ao nodes vizinhos. O conceito de descrever sua infraestrutura em forma de código chama-se infrastructure as code.
O Docker é a maneira mais fácil de criar um container. Basta rodar o Dockerfile (arquivo no qual você descreve sua infraestrutura) e adicionar o node ao load balancer. Exemplo:
Stateful vs Stateless
Pronto! Minha aplicação está escalável horizontalmente, rodando atrás de um load balancer e utilizando Docker. Agora acabou, certo? CERTO?! Não. Aqui as coisas começam a ficar um pouco mais complicadas. O maior erro na hora de escalar é simplesmente seguir soluções baseadas em StackOverflow e não olhar para as necessidades da sua aplicação.
Sua aplicação é Stateful ou Stateless? Esses dois termos se referem ao estado das entidades da sua aplicação. Aplicações Stateless não precisam que o estado de entidades estejam armazenados com o servidor para operarem. Aplicações Stateful dependem de informações armazenadas no servidor para conhecerem o estado do cliente.
Confuso? Um pouco. A implementação de estados e a definição podem variar de linguagem pra linguagem. De forma simples, aplicações Stateful armazenam sessões HTTP e objetos do cliente no servidor para serem recuperadas em requisições futuras, enquanto aplicações Stateless não dependem desses objetos, fazendo com que o cliente defina o seu estado e se autentique em toda requisição.
No modelo de escalabilidade horizontal o Load Balancer pode servir a requisição do cliente para qualquer um dos nodes. Em uma aplicação Stateful, a sessão do cliente e todos os objetos daquela sessão são armazenados no servidor em que o login foi efetuado. Porém, a requisição seguinte pode ser servida para um node que não possui aqueles objetos de sessão, enviando o cliente de volta pra tela de login. O que fazer nesse caso?
Replicação de Sessão e Armazenamento Externo
As duas soluções acima são as mais comuns para resolver o problema de sessão em aplicações Stateful.
Na replicação de sessão os nodes compartilham entre si os objetos stateful, possuindo um cópia em cada um dos nodes. Dessa forma, não importa o node que o cliente acesse, ele sempre vai ter acesso ao estado das suas entidades. Porém, isso cria um novo problema: quanto mais clientes, maior o tamanho do cache de objetos e mais memória é necessária em cada um dos nodes. Em aplicações Java, o JVM Heap Storage cresce e isso aumenta o uso do Garbage Collector, levando o consumo de CPU lá pra cima.
Ora pois, então não vamos colocar a sessão no servidor de aplicação. Surge a possibilidade de utilizar armazenamento externo. Redis, MemCached, MongoDB e muitos outros sistemas in-memory são utilizados pra armazenar objetos de aplicações Stateful. Os objetos ficam em um servidor externo e o servidor de aplicação requisita conforme necessário. Mas, como nem tudo são flores, um novo problema surge: custo. Não é apenas um servidor a mais. Você não quer que a sessão do seu usuário dependa apenas de um servidor. Da mesma forma que você criou todos os mecanismos necessários pra deixar sua aplicação escalável e com alta disponibilidade, esses mecanismos devem ser replicados ao seu servidor de cache, muitas vezes dobrando o custo de infra.
Aqui as coisas começam a ficar um pouco… grudentas.
Nada de mexer com o servidor de aplicação e caches. Sticky Session é a resposta mais simples e fácil. Através de uma configuração, o Load Balancer se certifica que o mesmo cliente seja sempre redirecionado para o mesmo node das suas requisições anteriores, isto é, até que a sessão local dele se expire.
Essa configuração é geralmente feita através de cookies. Dessa forma, o cliente vai sempre ter acesso a sua sessão no node. A essa altura do campeonato você está se perguntando qual o problema com isso. Eu te digo: deployment e fail over. Como a sessão permanece no servidor, ao fazer deploy da aplicação, a sessão é invalidada e o cliente é forçado a fazer login novamente. Quanto ao fail over, imagine que o node em que o cliente está sempre conectado morre por algum motivo. O cliente é redirecionado para o login.
Lembra que eu falei da importância de olhar para a própria aplicação antes de implementar um modelo de scaling? Os tópicos acima são os motivos. Não existe a melhor solução pra scaling, existe a que melhor se adapta ao seu tempo, orçamento e carga.
Apesar de aplicações Stateless serem mais fáceis de escalar, aplicações Stateful são mais fácil de desenvolver, deixando as interações entre o cliente e servidor mais dinâmicas, produzindo menos código e remenda pra conectar módulos e páginas. Novamente, o quão mais fácil (e se são mais fáceis) vai depender da aplicação e caso de uso.
Implementando tudo isso de forma prática
Pra colocar tudo isso em prática recomendo a utilização do AWS Beanstalk. A Amazon Web Services oferece diversas formas de escalar uma aplicação de forma horizontal. O Beanstalk é automatiza a criação desse ambiente, criando containers, load balancers e escalando de forma automática. A solução é um PaaS criado para equipes DevOps que precisam automatizar processos de deploy sem se preocupar demais com infra.
Wrapping up
Ficou com dúvidas em algum tópico? Quer um artigo mais aprofundado em algum desses assuntos? Falei alguma besteira? Me mande uma mensagem no LinkedIn.
Over and Out.
