Escalabilidade, clusterização e atualizações sem interrupção
Quando iniciamos com desenvolvimento WEB na Kugel existiam poucas aplicações e, por consequência, poucos usuários utilizando o ambiente WEB. Assim que o projeto foi tomando corpo, mais usuários começaram a utilizar o sistema, operações mais críticas começaram a ser utilizadas e estávamos com um problema: O sistema ficava indisponível durante a atualização.
O sistema estava rodando em apenas um servidor e para atualizar a aplicação ela era desligada, a nova versão era copiada para o servidor e a aplicação era iniciada novamente. Nesse ambiente os clientes se comunicam diretamente com o servidor de aplicação (figura 1).
A solução foi incluir um balanceador de carga (load balancer) antes do servidor de aplicação. O balanceador recebe a requisição do cliente, cria uma nova requisição para qualquer um dos servidores de aplicação, assim que tiver o retorno da aplicação o balanceador retorna o resultado para a requisição do cliente (figura 2).
Na figura cada servidor de aplicação é uma máquina física separada, mas pode ser uma máquina virtual, um container docker ou simplesmente um outro processo.
Essa arquitetura também é conhecida como clusterização e permite que o software tenha uma escala horizontal, onde é possível incluir ou remover servidores de acordo com a demanda sem que a aplicação fique indisponível. Na arquitetura anterior só seria possível fazer escala vertical, melhorando o hardware do servidor geralmente com um custo mais elevado e tirando a aplicação do ar. Outra vantagem da clusterização é que se algum servidor de aplicação cair a aplicação não ficará totalmente indisponível.
Com esse ambiente é possível atualizar um servidor de aplicação por vez, sem que o sistema fique indisponível. Porém a aplicação deve ser stateless, ou seja, a aplicação não pode guardar dados de nenhum cliente/requisição no servidor em que a aplicação está rodando, pois o balanceador pode enviar a próxima requisição do mesmo cliente para outro servidor que não terá esses dados. Até existe a possibilidade de configurar o balanceador com sticky session, assim o balanceador sempre envia as requisições de um determinado cliente para o mesmo servidor, se o servidor estiver disponível, mas optamos por deixar nossa aplicação completamente stateless.
Em nossa aplicação apenas a autenticação não era stateless. As sessões eram gravadas em um arquivo temporário no servidor e quando o balanceador enviava a requisição para outro servidor era como se o usuário não estivesse autenticado, redirecionando o usuário para a página de login. Então alteramos a autenticação para utilizar JWT (JSON Web Tokens) guardando alguns dados da sessão no token e garantindo que a autenticação foi realizada por algum dos nossos servidores.
Incluindo o balanceador de carga conseguimos atualizar sem interrupções e ainda ganhamos escalabilidade, permitindo que o sistema tenha um hardware disponível de acordo com a demanda. Claro que com esse ambiente só temos escalabilidade com relação ao servidor de aplicação.
Seguem alguns links para quem quiser se aprofundar mais no assunto: