DevSecOps

11 mai, 2016

Conheça o Zero-Downtime Deploy com Tomcat

Publicidade

O termo “downtime” é usado na engenharia de software para referir-se a períodos em que um sistema não está disponível. O tempo de inatividade ou duração da interrupção se refere a um período de tempo em que um sistema não fornece ou executa sua função principal. A indisponibilidade é a proporção de um período de tempo que um sistema não está disponível ou off-line.

Isso, geralmente, é um resultado do sistema que deixou de funcionar por causa de um evento não planejado, ou por causa da manutenção de rotina (um evento planejado). O termo é geralmente aplicado a redes e servidores. Os motivos mais comuns para interrupções não planejadas são falhas do sistema (como um acidente) ou falhas de comunicações (vulgarmente conhecido como falha de rede). Foi então que se criou-se o tempo “Zerodowntime deploy” que é ato de se atualizar um nova versão de um sistema sem interromper o serviço.

Como usar no Tomcat?

Desde a versão do tomcat7x, existe esse recurso disponível para se atualizar um novo war, sem parar e atrapalhar as pessoas que estão naquele exato momento penduradas em sessões HTTP. O tomcat gerencia as versões através do próprio nome do arquivo usando a seguinte sintaxe: “##001”. Muito fácil, ao invés de fazer o deploy um “sistema.war”, faça como “sistema##001.war”. O contexto de acesso na URL vai continuar o mesmo, ou seja, tudo que estiver depois do ## não sera considerado como parte da nomeação, mas internamente o tomcat estará gerenciando as versões.

É aí que vem a mágica: quando chegar a hora de atualizar uma nova versão da solução, faça o deploy usando a versão 2: “sistema##002.war” e assim sucessivamente. O tomcat vai subir a versão 2 da solução simultaneamente a versão 1. Todos os clientes que, partir desse momento, acessarem a solução, será automaticamente redirecionado para a nova versão, e todos os clientes que estavam pendurados na versão 1 vão continuar usando como se nada tivesse acontecido. A medida que esses clientes pendurados na versão 1 têm sua sessão HTTP encerradas, eles irão automaticamente sendo redirecionados para a versão 2. Seguindo este modelo, é só uma questão de tempo para a versão 1 ir perdendo sessões ativas, e a versão 2 assumir toda a demanda. Depois que esse tempo passar e a versão 1 estiver sem nenhuma sessão, você já pode parar e remover a versão 1, deletando o “sistema##001.war” diretamente da pasta ou parando o contexto via manager mesmo. Veja alguns artigos sobre:

Como o tomcat controla isso?

Quando é feito o deploy de uma solução gerenciando versões com #00x, o tomcat vai acrescentar o novo  header HTTP em todas as requisições chamado de VERSION, que indicarão qual é a versão da sessão pendurada. Podemos ter quantas versões simultâneas de war for necessário, desde que os wars estejam devidamente versionadas e o tomcat tenha recursos para rodar todas.

Dicas de otimização

Já faz um tempo que tenho usado esse recurso em ambiente de produção com sucesso e assim desenvolvi algumas manhas bem interessantes para melhorar e otimizar todo o processo. Veja a seguir:

1. Global DataSource

Não use DataSource local, pois a cada deploy de uma nova versão de war, será criado um novo DataSource a toa, gastando memoria desnecessariamente. Para contornar isso, use DataSource globais que são criados e carregados apenas uma vez, reusando dentro de todas as versões que venham a ser simultaneamente executados.

2. Global Classpath

Não use jars de terceiros na lib do projeto, pois a cada deploy de uma nova versão de war, todos os jars serão recarregados a toa, gastando memoria desnecessariamente. Para contornar isso, coloque todos os jars que a solução na lib global do tomcat, no qual será carregado apenas 1 vez e reutilizado dentro de todas as versões que venham a ser simultaneamente executadas.

3. Semáforo nos Jobs

Caso a solução tenha jobs Java e venha a ter versões sendo executadas simultaneamente, isso resultara em processamento duplicado. Para contornar isso, é necessário implementar um algoritmo de semáforo responsável por impedir processamento de jobs simultâneas. Existem três opções:

  1. Look SGBD: use bloqueio pessimistas de banco de dados;
  2. Look de Arquivo: use bloqueio de arquivos;
  3. Look de Objeto: use bloqueio de objetos JNDI.

Depois de usar todos, a melhor opção para mim foi a 2. Usando NIO, o código funciona super rápido e é portável para qualquer servidor. A opção 1 é legal, mas tem o round-trip no banco que apresenta um tempo maior de processamento. A opção 3 é muito boa, mas você acaba tendo que registrar os beans no JNDI que têm uma implementação diferente para cada servidor.

Confiabilidade, disponibilidade, recuperação e indisponibilidade são conceitos relacionados aos eventos “não planejados” que eu deixarei de fora, uma vez que o foco do artigo é a atualização da solução, sendo um “evento planejado”.

Conclusão

Mesmo se você precisar alterar seu produto para se adaptar a essa operação, eu tenho certeza que valerá a pena. Experiência própria! Imagine você dizendo ao seus clientes e/ou seu gestor que o serviço da solução não será mais interrompido por atualização de novas versões? Bem conversado, fazendo um bom marketing, é motivo de um bom aumento de salário da equipe.