segunda-feira, 10 de dezembro de 2012

O papa-filas e o round-robin de DNS...

Dando seqüência ao tema "balanceamento de carga", vou agora mostrar os tipos mais comuns e suas vantagens e desvantagens, de forma a suprir o leitor com subsídios para uma escolha adequada ao seu caso.



1) Balanceamento por Round-Robin de DNS

O algoritmo de "round-robin" não é nenhuma novidade para profissionais de informática. Em qualquer curso de faculdade ele é assunto freqüente.  É simplesmente uma lista, enfileirada, em que os elementos vão, um a um, assumindo certa posição. Ao chegar no último elemento, o processo reinicia com o primeiro elemento. O DNS (Domain Name System) é uma forma de traduzir endereços de sites em seus respectivos IPs, por exemplo: www.google.com.br vira 74.125.234.159. O balanceamento de carga via round-robin de DNS funciona redirecionando alternadamente o usuário para uma lista de servidores, cada vez que ele acessa a mesma URL pelo navegador. Ou seja, há mais de um IP respondendo a uma mesma URL, só que de forma alternada.

Funciona de maneira muito similar ao "papa-filas" do supermercado, onde cada cliente entra numa única fila e ao final é direcionado para uma das gôndolas de pagamento disponíveis.

Se forem três servidores de aplicação, cada vez que o usuário recarregar a página no navegador, será direcionado para um dos servidores. Se chegar no último servidor e recarregar a página, será redirecionado novamente ao primeiro servidor.

Essa é uma forma bastante simples de balanceamento, que não leva em consideração a carga de cada servidor de aplicação, ou seja, não é um balanceamento ótimo, porque uma máquina lotada vai continuar recebendo usuários. Além disso, caso uma das máquinas falhe e fique fora do ar, ela mesmo assim ainda estará participando da fila. E usuários serão redirecionados para ela assim mesmo, recebendo a mensagem de que o servidor está fora do ar. Em outras palavras, um certo grupo de usuários cairá na máquina que está ruim.

Apesar das desvantagens, é uma forma extremamente fácil de implementar e é recomendável que ela exista em conjunto com outras formas de balanceamento.

No próximo post falarei sobre balanceamento de carga por cluster.




sábado, 8 de dezembro de 2012

Quantidade pode ser melhor que qualidade? Entenda como funciona o balanceamento de carga.

Há alguns anos atrás, participei de um projeto de um sistema que tinha uma previsão de alto número de usuários simultaneos pela rede. Sugeri ao líder do projeto que fizesse um grupo de servidores de aplicação, fazendo balanceamento de carga entre eles, ou seja, montar um conjunto de dois ou mais computadores rodando a mesma aplicação, que dividissem o trabalho de atender àqueles usuários do serviço.
 
O líder do projeto não adotou a sugestão, argumentando que "tínhamos um servidor parrudo", com muita memória e vários processadores, capaz de suportar tamanha demanda. Contra-argumentei que, apesar de toda a indubitável capacidade do servidor, ele era um só. E se falhasse, o sistema ficaria fora do ar.
 
Mas, infelizmente, o líder do projeto não tinha visão orientada à otimização. E disse que o que deveríamos fazer era preparar um servidor de contingência. A visão dele era assim: "Temos um servidor que aguenta o tranco. Mas, no caso de não aguentar, colocamos outro, igual, para assumir a questão".
 
O resultado da história foi que o sistema foi posto no ar dessa forma. E o tal servidor "parrudo", à medida que o número de usuários aumentava, foi ficando lento, lento.... Até que chegou uma hora que o sistema caiu. E foi aquela correria! Inicialmente reinicializou-se o servidor várias vezes, mas os usuários retornavam (obviamente) e o sistema caía de novo.
 
Ligou-se a contingência. Ficou assim: O primeiro servidor entrava no ar. Os usuários acessavam o sistema, até que ele caía. Aí os próximos usuários eram direcionados ao segundo servidor, que também entupia e caía.
 
O tal líder do projeto culpou a tudo e a todos. Disse que o sistema operacional não prestava, reclamou do software do servidor de aplicação, da linguagem em que foi desenvolvido o sistema, maldisse o fabricante dos processadores do servidor, enfim, criou uma porção de desculpas para a coisa não ter funcionado. Mas em nenhum momento cogitou a hipótese de estar errado.
 
Sua vontade era reescrever o sistema em outra linguagem, funcionando sob outro sistema operacional, mas permanecendo o ambiente de um único servidor com outro de contingência.
 
Ele tinha o apoio da empresa, que confiava na sua competência técnica. E realmente ele tinha muita competência técnica, inegavelmente. Mas lhe faltava algo de igual importância: Maturidade.
 
Bem, o sistema ia pro ar uma vez por ano. Durante dois anos permaneceu assim. Funcionava mal, os usuários tinham uma péssima experiência e havia um alto nível de estresse.
 
Finalmente, o líder do projeto decidiu começar a buscar otimizar o sistema e isso promoveu uma certa melhora. Mas ele ainda não havia sido convencido da necessidade de dois ou mais servidores.
 
Em um belo dia de sol, mudaram o líder do projeto. Eu permaneci no grupo de desenvolvedores do sistema. Após uma longa conversa com a nova líder do projeto, consegui convencê-la da importância de ter um modelo com dois ou mais servidores de aplicação, ao invés do modelo de servidor+contingência.
 
Infelizmente a empresa foi contra. Então, junto com um colega de equipe, montamos um cluster de servidores de aplicação, utilizando máquinas velhas do nosso setor, abandonadas, cacarecos mesmo, mas fazendo balanceamento de carga. Fizemos testes então, com softwares que simulam acessos simultâneos de usuários, sobre nosso cluster de cacarecos. E... adivinhe? A estabilidade era maior do que em uma situação de um único "servidor parrudo".
 
Os dados eram incontestáveis e foi o argumento que convenceu a empresa a adotar tal procedimento. Hoje em dia todo sistema da empresa que precise de alto desempenho e disponibilidade utiliza mais de um servidor, em cluster (usamos várias máquinas virtuais, fazendo balanceamento).
 
Eis um caso em que quantidade é melhor do que qualidade.
 
No próximo post aprofundarei este tema, mostrando os tipos de balanceamento de carga possíveis de implementar. Não perca!

sexta-feira, 7 de dezembro de 2012

O que o xadrez de rua tem a ver com sistemas rápidos?


A técnica que vou ensinar neste post - e é uma das mais recentes utilizadas em nossos sistemas, embora não seja nenhuma novidade - chama-se "query simulada".

Mas antes vou falar sobre um filme que assisti há alguns anos atrás, chamado "Lances Inocentes" (Searching for Bobby Fischer).
 
O filme conta a história de um garoto que tem um talento especial para o xadrez e é incentivado pelo pai a participar de competições. A família do menino contrata um instrutor que passa a treiná-lo sob uma abordagem clássica do xadrez. Só que simultaneamente, o garoto conhece um enxadrista de rua, que também o influencia, mas sob a ótica do "speed chess", com técnicas bastante heterodoxas, consideradas pelo instrutor do menino como de segunda categoria. E instala-se o conflito.
 
Mas o que, afinal, tem a ver o xadrez de rua com sistemas rápidos?
 
Bem, no contexto de sistemas, sempre existiu uma discussão sobre boas práticas de programação. Há um conflito entre o meio acadêmico e quem "põe a mão na massa".
 
Nesse contexto, todos sabemos que colocar informações hard-coded no sistema (junto com o código-fonte) é uma prática péssima de programação, certo?
 
Bem, o enxadrista de rua do filme tinha seu ponto de vista. E sua técnica funcionava em determinadas ocasiões. E ajudou o menino a encontrar um meio termo.
 
Portanto, estou dizendo que tudo na vida depende do contexto. Em uma situação em que a velocidade de resposta é condição sine qua non, aceito sim ser um pouco heterodoxo.
 
Isto posto, vamos ao que interessa: O que afinal é uma "query simulada"?
 
De forma simples, é programar o resultado de uma query hard-coded no sistema. Posso dizer que é uma espécie "query cacheada" que não expira e que ocupa a memória pontualmente, por pouco tempo. A vantagem não podia ser outra: Velocidade, disponibilidade, menor probabilidade de falhas.
 
Fazemos isso pegando um método que retorna uma query real, indo efetivamente ao banco de dados, substituindo-a por uma query simulada. Para o resto do sistema, é como se estivéssemos indo realmente ao banco de dados. O retorno continua sendo um recordset, mas nós o preenchemos manualmente e o sistema é induzido a acreditar que os dados vieram mesmo do banco. E não tem mesmo diferença nenhuma.
 
Essa foi uma forma de otimizarmos ainda mais o que já havia sido otimizado em nossos sistemas.
 
Estudamos as queries elegíveis para "query simulada" e as convertemos, é claro, com muito critério.
 
Como exemplo, imagine uma query que vai ao banco de dados obter os estados federativos brasileiros e que é muito utilizada. É possível criar uma query fake no sistema que contenha a lista dos estados. O acesso é infinitamente mais rápido do que indo ao banco. Evita tráfego de rede, vai continuar mostrando os estados mesmo que o banco esteja fora do ar, enfim, no contexto de um sistema que não pode ter gargalos, é uma técnica a mais no rol das possibilidades., e que não deve ser ignorada.

Fique ligado que ainda tem muito mais pela frente! Até o próximo post.

quarta-feira, 5 de dezembro de 2012

Outro pulo do gato: O query timeout!

Caros leitores, hoje falarei sobre algo não menos importante do que o assunto tratado no post anterior. Considero que é também outro "pulo-do-gato" em se tratando de otimização de sistemas web: O "timeout de query"!
 
Tomando-se a velha premissa como base, ou seja, que o banco de dados é um recurso finito, disputado, e portanto sujeito a falhas, e considerando que uma query pode levar mais tempo do que o desejado para executar, então faz-se necessário o uso do "timeout de query".
 
A questão é que, se uma query demorar muito para retornar um resultado, o servidor de aplicação enfileirará requisições, o que é nosso maior temor - e deve ser evitado a qualquer custo.
 
O "timeout de query" funciona de forma muito similar ao "cache de queries". Mas, o intuito aqui é especificar um tempo que nossa aplicação considerará aceitável para a execução de uma query.
 
Em cada query do sistema, especifica-se em segundos, o tempo que aceitaremos para sua execução. Caso esse tempo seja atingido, o servidor de aplicação considerará que houve uma exceção. Um erro, provocado por estouro de tempo limite na execução da query.
 
Esse erro deve ser esperado e tratado pela nossa aplicação, que, em geral, informará ao usuário que a sua requisição não foi atendida dentro do tempo esperado.
 
A grande vantagem é que o usuário não tem a percepção de um sistema lento, agarrado, pelo contrário, para ele a aplicação continua no ar e respondendo adequadamente. Só que agora informando o que ocorreu e permitindo que ele tente novamente.
 
Sendo um dos grandes segredos de um sistema web estável e com alto uptime, o "query timeout" é  uma prática indispensável. Todas as queries do sistema devem utilizá-lo, sem exceção!
 
Até a próxima!

domingo, 2 de dezembro de 2012

Eis o grande segredo...

Hoje vou falar sobre uma das formas mais eficazes de otimização de qualquer sistema web: Apresento-lhes o "Cache de Queries".

No post anterior, discerni sobre a importância da otimização do banco de dados e sobre a preocupação que se deve ter com o tempo de execução de cada uma das queries que fazem parte de um sistema.

Portanto, o banco de dados é o ponto de maior geração indireta de gargalos em um sistema web. Se ele estiver lento, todo o resto ficará lento, através de um efeito cascata. O acesso ao banco é o maior problema.

Vamos então "eliminar o problema".

"Mas como assim? Afinal o banco de dados é o cerne do sistema. Sem ele não faria sentido o sistema!" - já ouço o leitor retrucar.

Na verdade, não estamos falando, obviamente, em eliminar o banco de dados. Estamos falando em evitá-lo o máximo que pudermos!

Quanto menor for o número de vezes que a aplicação precisar "encostar" no banco de dados, melhor para a performance geral do sistema web.

E é isso exatamente o que faz o Cache de Queries! Evita "idas" desnecessárias ao banco de dados!

O Cache de Queries é algo realmente modificador de vidas. Eu diria que um sistema pode ser dividido entre antes e depois da implementação de Cache de Queries.

O Cache de Queries funciona da seguinte maneira: Você especifica deliberadamente que uma determinada query SQL será "cacheada", isto é, posta em memória real. Então, na primeira vez que a query é executada, o servidor de aplicação efetivamente requisita a consulta ao banco de dados. Entretanto, o resultado de retorno dessa consulta, ou seja o recordset obtido em resposta, é armazenado em memória no servidor de aplicação. Isso significa dizer que, a partir deste momento, se algum usuário requisitar novamente essa mesma query, o banco de dados não será mais incomodado.

O que posso dizer é que o aumento de performance com a técnica é gritante.

As medições de antes e depois da implementação de Cache de Queries mostram situações em que o tempo de carga total de uma página com a query cai de 1 ou 2 segundos para ZERO. É óbvio que algum tempo é gasto no processo para recuperar os dados, ainda que estejam em memória. Mas como a unidade de medida é o milissegundo, o que o servidor de aplicação nos diz é que o tempo gasto foi de zero milissegundos, ou seja, a consulta levou menos de 1 milissegundo para ser executada!

"Uau! Vamos então cachear todas as queries do sistema e resolver todos os nossos problemas!"

Não é bem assim... Há situações que não permitem que uma query seja cacheada. Há de se analisar query por query do sistema, e definir quais delas são elegíveis para o cache. Vamos supor, por exemplo, que o resultado de uma pesquisa dependa de dados que estejam sendo alimentados pelo usuário do sistema. Para ilustrar: Suponha que se esteja pesquisando um aluno pelo seu nome, em uma lista de alunos inscritos. Se é um sistema de inscrição de alunos, e se pusérmos o retorno da pesquisa em cache, alunos inscritos posteriormente ao cacheamento não aparecerão no resultado da pesquisa.
 
Além disso, lembrando de nosso amigo Lavoisier, se ganhamos de um lado, perdemos de outro. Nesse contexto que estamos trabalhando, o ganho é de extraordinária performance. Em detrimento de um uso maior de memória real no servidor de aplicação.

Como sempre, é um ajuste fino. Há que ser estudado com cautela. Mas, como sempre, a relação custo x benefício é positiva.

As linguagens em geral, permitem também, que seja especificado quanto tempo a query ficará em cache, em segundos, minutos, horas ou dias. O resultado de certas queries podem ficar "eternamente" (365 dias) em cache. Por exemplo, no contexto da educação, a query que retorna as séries em determinado ano letivo é uma delas. Como a única chance de ser criada uma série nova é na virada do ano, então a lista das séries pode ficar o ano inteiro em cache.

A conclusão é que, um sistema que não usa cache de queries está aquém do desejado e vai sofrer com certeza, de lentidão, quiçá terá comprometido seu uptime.

Minha opinião pessoal é que todo sistema deve ser desenvolvido com esta técnica desde o princípio, em função dos resultados excepcionais alcançados para o usuário final.
 
No próximo post o assunto será algo igualmente importante: O time-out de queries! Até lá.