Serviços de Informações da Internet

Dimensionando aplicativos ASP.NET: lições aprendidas

Richard Campbell

 

Visão geral:

  • A importância da colaboração do desenvolvedor/TI
  • Compreendendo as principais estratégias de dimensionamento
  • Compartilhando o conhecimento entre as equipes
  • Definindo um conjunto básico de conhecimento

Sumário

Os conceitos básicos do dimensionamento
O encontro das idéias
O que o sistema de rede precisa saber quanto ao desenvolvimento
O que o desenvolvimento precisa saber quanto ao sistema de rede
Voltando ao dimensionamento
O combate conjunto a incêndios

Como consultor, todos os aplicativos ASP.NET dimensionados com êxito nos quais já trabalhei foram o resultado de um esforço colaborativo entre os desenvolvedores (quem os criou) e os administradores de rede (quem os executa de fato). Infelizmente, nem sempre fica claro no início do

ciclo de vida de um aplicativo que essa colaboração é essencial. Assim, quase nunca tive uma chance de me envolver no início do ciclo de vida de um aplicativo – apenas no final, quando o problema já existe.

A verdade é que nenhum aplicativo é dimensionado efetivamente na primeira vez – é impossível ter tudo certo desde o começo. Para dimensionar um aplicativo com êxito, você precisa compreender como ele foi criado, bem como de que forma funciona o ambiente operacional em que ele é executado. Em outras palavras, você precisa de informações tanto do pessoal do desenvolvimento quanto do pessoal de rede. Sem esse conhecimento compartilhado, não é possível obter êxito.

Os conceitos básicos do dimensionamento

Antes de nos aprofundarmos, montemos o palco para o que é necessário ao efetivo dimensionamento de um aplicativo ASP.NET. Há duas estratégias básicas empregadas normalmente: especialização e distribuição. E grande parte dos aplicativos ASP.NET grandes, dimensionados, aplicam ambas as estratégias. Além disso, todos os truques existentes por aí para ajudar no dimensionamento do aplicativo ASP.NET acaba se dividindo em uma ou em outra.

A especialização se resume a separar elementos do aplicativo para que eles possam ser dimensionados de maneira independente. Por exemplo, você talvez queira criar servidores de imagens dedicados, e não usar os mesmos servidores que processam páginas ASP.NET. A configuração ideal de um servidor de imagens é bem diferente em relação a um servidor ASP.NET. Além disso, a separação das solicitações de imagem do restante do aplicativo cria a possibilidade de usar recursos de terceiros no oferecimento de imagens. A mesma abordagem pode ser aplicada aos demais arquivos de recurso.

Por outro lado, a distribuição envolve a difusão simétrica do aplicativo em vários servidores, normalmente chamados de web farm. Os aplicativos ASP.NET são especialmente adequados à distribuição porque cada solicitação de página individual é relativamente pequena, e as interações de um determinado usuário são, em grande medida, independentes dos demais usuários. A distribuição é mesmo a manifestação da filosofia de "redução da escala", em que vários servidores de desempenho médio funcionam juntos para atender aos usuários, e não a abordagem de "aumento de escala" em que há único servidor possante fazendo tudo.

A combinação entre especialização e distribuição aumenta a eficiência – é possível distribuir apenas os elementos do aplicativo que precisam de desempenho adicional. Por exemplo, caso você tenha criado servidores de imagens especializados, mas o oferecimento delas continua inadequado, é possível adicionar servidores de imagens, e não servidores para todo o aplicativo. É importante manter essas estratégias em mente enquanto você procura aumentar o desempenho e a escalabilidade do aplicativo ASP.NET.

O encontro das idéias

Em algum ponto do ciclo de vida de todo aplicativo ASP.NET, os desenvolvedores e a equipe de rede/TI devem se encontrar. Com alguma sorte, isso acontecerá antes da implantação do aplicativo, mas às vezes só acontece depois que já há uma crise – por exemplo, quando o aplicativo é executado sem problemas no ambiente de teste, é remetido para os usuários e pára. (É quando chamam consultores como eu.)

Quando os desenvolvedores e o pessoal da rede se encontram, o objetivo principal é trocar informações. Os desenvolvedores têm conhecimento essencial a respeito do aplicativo, assim como a equipe de rede tem sobre o ambiente operacional. Cada grupo precisa compreender os dados do outro, e quanto mais cedo eles se reunirem durante o ciclo de vida do aplicativo, melhor.

Essa reunião não deve ocorrer pela primeira vez durante uma crise. Sem uma compreensão básica entre as duas equipes, é extremamente difícil imaginar por que o aplicativo não está atendendo aos requisitos. Além disso, é muito fácil se defender achando que o problema está única e exclusivamente na outra equipe. E isso simplesmente nunca é verdade – invariavelmente, as duas partes são necessárias para resolver qualquer problema significativamente complexo.

No entanto, mesmo nos bons momentos, uma reunião assim pode ser um grande desafio. As que organizei normalmente começavam com o pessoal da rede de um lado da mesa e o pessoal de desenvolvimento de outro. E um começa a olhar para o outro. Para começar a conversa, ressalto o objetivo da reunião, mais especificamente uma troca de conhecimentos. Não importa quem começa. Começarei com o que o sistema de rede precisa saber do desenvolvimento.

O que o sistema de rede precisa saber quanto ao desenvolvimento

Todo aplicativo ASP.NET tem características próprias, embora haja elementos essenciais que se aplicam a qualquer caso. O arquivo web.config é um deles (veja a Figura 1). Web.config é um ponto de interseção entre o desenvolvimento e o sistema de rede. Às vezes, ele é configurado pelos desenvolvedores e, em outras, pela equipe de rede. De uma forma ou de outra, o conteúdo de web.config estabelece os limites quanto à configuração do hardware e da rede, afetando diretamente o modo de funcionamento do aplicativo. Explorar detalhadamente todos os mínimos aspectos do arquivo web.config seria o bastante para escrever um livro inteiro; aqui, a questão é que ambos os grupos precisam estudar o arquivo web.config e chegar a um consenso quanto ao que a configuração representada por ele significa e em relação ao impacto que várias configurações terão sobre o aplicativo e o ambiente.

fig01.gif

Figura 1 Um web.config básico mostrando algumas configurações do aplicativo e uma configuração de erro personalizada (clique na imagem para ampliá-la)

Por exemplo, a seção <authorization> de web.config especifica como os usuários serão autenticados no aplicativo e, assim, define uma dependência. Se o aplicativo usar a autenticação do Windows®, poderá depender do Active Directory®. Caso ele use a autenticação baseada em formulários, a dependência será de um repositório de dados de contas de usuários. Isso certamente vale uma conversa.

A seção <customErrors> é digna de nota porque afeta a maneira como as falhas aparecem para os usuários. Não se trata de uma configuração complexa, mas vale a pena discuti-la apenas para compreender como serão as páginas de erro. Em um momento inicial do ciclo de colaboração, é provável que não haja nenhuma página de erro personalizada – também vale a pena conversar sobre isso.

A seção <appSettings> de web.config pode ser especialmente significativa. É nela que os desenvolvedores costumam armazenar valores globais como cadeias de conexão para bancos de dados. Trata-se de uma grande fonte de dependências, além de ser parte essencial do planejamento de failover, migração etc. Mas, por ser totalmente personalizada, a seção <appSettings> pode conter praticamente qualquer coisa e talvez exija muita explicação para que se compreenda o conteúdo. Quase sempre há órfãos nesta seção – valores que não são, de fato, usados em lugar algum no aplicativo.

Mesmo que os desenvolvedores não estejam usando <appSettings>, o pessoal de rede/operações poderá querer que eles usem – tendo todas as cadeias de conexão de banco de dados, há uma maneira eficiente de criar uma estratégia de failover simples. Caso o servidor do banco de dados seja desativado, poderá ser inserida uma cadeia de caracteres de substituição a fim de apontar para um servidor de banco de dados diferente. Aproveitar essa oportunidade logo no início do ciclo de desenvolvimento pode aumentar a confiabilidade e a facilidade de manutenção do aplicativo.

Por fim, você deve observar que um valor absolutamente essencial no web.config do ponto de vista do dimensionamento é a marca <sessionState>, que determina onde os dados da sessão serão armazenados para o aplicativo. Por padrão, os dados da sessão são armazenados em "InProc", ou no espaço do processo do aplicativo ASP.NET. Caso os dados da sessão estejam configurados como em processo, isso significa que todo balanceamento de carga deve ser "adesivo" – um determinado usuário deve sempre ser atendido pelo mesmo servidor em que os dados da sessão residem. Há uma intensa conversa entre os desenvolvedores e o pessoal de rede porque isso tem impacto direto na forma como você dimensiona e usa o failover. Falar sobre isso logo pode evitar muitas dores de cabeça durante a tentativa de depuração do aplicativo.

Quando a conversa chega ao estado de sessão, é fácil atender aos requisitos do balanceamento de carga do aplicativo em geral. Alguns ambientes contam com hardware para balanceamento de carga dedicado com recursos específicos – mas se o aplicativo não puder tratar esses recursos, eles não significarão muito. Encontrei situações em que o balanceamento de carga do hardware estava sendo usado com um aplicativo ASP.NET que apresentava dados da sessão em processo. A conseqüência foi a perda ocasional de sessões sem nenhuma explicação. Parecia um bug no aplicativo, mas não era – tratava-se de um equívoco na configuração.

O pessoal de rede precisa saber claramente do desenvolvimento quais esquemas de balanceamento de carga funcionarão no aplicativo. Essa é muito mais uma conversa de duas vias a respeito de quais são os esquemas de balanceamento de carga disponíveis, bem como o que o aplicativo pode tolerar. Uma grande vantagem de ter essa conversa logo no início do ciclo de vida do aplicativo é que você poderá planejar com antecedência recorrer a um esquema de balanceamento de carga sem afinidade, fora do processo.

No momento em que um aplicativo ASP.NET estiver pronto para implantação (ou já estiver na implantação inicial), a equipe de desenvolvimento terá uma boa idéia das áreas mais rápidas e mais lentas do aplicativo. E o mais importante: terá uma noção dos afunilamentos ou dos possíveis pontos de crise no sistema. Caso conheça esses afunilamentos, a equipe de rede/operações poderá se preparar para os problemas que se sucederão, e talvez até evitá-los.

Por exemplo, trabalhei uma vez com um aplicativo que apresentava um processo pesado de carga de dados todas as noites, durante o tempo de inatividade. Os desenvolvedores criaram, testaram e compreenderam como o mecanismo de carregamento dos dados funcionava. Eles sabiam que isso era muito desgastante para o banco de dados, embora fosse eficiente e uma boa forma de resolver o problema.

Mas eles jamais disseram à equipe de rede que esse processo ocorria ou que tinham programado sua execução à 1h00 – a mesma hora em que eram executados os backups do banco de dados. Este permanecia online, mas era executado muito mais lentamente durante o backup porque todas as transações enviadas para o banco de dados seriam mantidas no log de transações.

A causa do conflito só foi identificada após a falta de espaço em disco para o log de transações do banco de dados decorrente da combinação entre a carga de dados e o backup simultâneos. Passar o início da carga de dados para as 3h00 resolveu o problema por completo – mas somente depois de vários dias de crise.

Manter uma conversa logo no início a respeito desses tipos de itens da carga de trabalho no aplicativo pode evitar grandes problemas ao longo do caminho.

O que o desenvolvimento precisa saber quanto ao sistema de rede

O meu ponto de partida favorito quanto àquela parte da reunião em que o pessoal de rede explica seu mundo ao pessoal de desenvolvimento é o diagrama de rede. É muito comum que os desenvolvedores vejam a rede como mostra a Figura 2 – ou seja, apenas servidores Web, navegadores e a Internet.

fig02.gif

Figura 2 A visão simples do desenvolvedor em relação à rede (clique na imagem para ampliá-la)

É claro que a realidade é muito mais complexa. Um diagrama como o da Figura 3 está muito mais próximo da realidade, ainda que esteja simplificado. Ao observar a Figura 3, você imediatamente tem mais dúvidas, como, por exemplo, "de que forma os usuários de VPN (rede virtual privada) trabalharão com o aplicativo" ou "qual é a diferença de autenticação entre usuários internos e públicos" etc.

fig03.gif

Figura 3 A verdadeira rede que os desenvolvedores precisam compreender (clique na imagem para ampliá-la)

É óbvio que não basta simplesmente fornecer um diagrama de rede. Também é importante explicar a rede detalhadamente porque é impossível saber, apenas observando um diagrama, exatamente quais elementos terão impacto sobre o aplicativo. Você precisa abordar o processo de conexão real de usuários públicos, VPN e internos. E discutir as várias regras de firewall existentes, em especial em arquiteturas de rede ao estilo DMZ mais complexas, trará naturalmente problemas em potencial para o aplicativo.

Obviamente, seria vantajoso manter todas essas discussões antes da implantação do aplicativo, até mesmo antes do início do desenvolvimento – mas não importa, a discussão tem de acabar acontecendo, e contar com todos à mesa para compreender todo o diagrama de rede é a chave.

O sistema de rede também é a área de modelos de failover e redundância, mas sem um suporte a software ou pelo menos o reconhecimento do comportamento, esses modelos raramente funcionam como planejado. Deve acontecer uma discussão detalhada de como seriam os vários cenários de falha. Se houver clustering in-loco no banco de dados, isso afetará o código relacionado ao acesso ao banco de dados. Por exemplo, é possível tentar novamente as consultas após uma alternância de servidor? Se houver um local redundante disponível, como os dados serão replicados até ele? Como ocorre uma alternância de um local para outro?

Mais uma vez, manter uma conversa logo no início ajuda, mas antes tarde do que nunca – todo o pessoal envolvido com o aplicativo precisa compreender como funcionam essas coisas. Não há nada mais frustrante do que contar com uma solução em failover que não funciona de fato quando necessário.

Por fim, existe um outro recurso de rede essencial que precisa ser compartilhado: os logs de produção. Sempre recomendo que os logs de produção sejam disponibilizados à equipe de desenvolvimento. A reação habitual do pessoal de rede a essa solicitação é "me peça que eu envio para você". Não acho isso adequado – é muito mais eficiente dar aos desenvolvedores a possibilidade de recuperar os logs por conta própria, normalmente em um site de backup.

Os logs de produção são essenciais durante uma crise. Eles costumam ser a melhor (e a única) fonte de dados reais, empíricos, sobre o que realmente aconteceu. Mas eles são igualmente importantes em circunstâncias mais comuns. Quando têm acesso habitual a logs de produção, os desenvolvedores podem verificá-los para saber de que forma um novo recurso está se comportando. E essa é uma ótima forma de descobrir que o recurso não está fazendo o que você esperava e corrigir o problema antes que haja uma crise. Quando todos têm acesso a logs, é possível reagir e corrigir os problemas mais rapidamente.

Voltando ao dimensionamento

A reunião entre a rede e o desenvolvimento se resume à compreensão de todo o escopo dos problemas que cercam o dimensionamento de um aplicativo ASP.NET. O ambiente real em que o aplicativo opera afeta diretamente como o código executado por ele funcionará. Todas as estratégias relacionadas ao dimensionamento terão impacto sobre o comportamento no ambiente. A aplicação de uma estratégia de especialização como, por exemplo, separar as partes relacionadas a SSL do aplicativo pode exigir alterações no ambiente do sistema de rede e talvez mudanças nos próprios servidores.

Mesmo uma alteração aparentemente centralizada no código usando o cache pode ter impacto no ambiente. Isso porque, ao adicionar o cache de dados ao aplicativo ASP.NET, você está diminuindo o número de chamadas do banco de dados em troca de um aumento no uso da memória. O resultado é que você pode aumentar o número de servidores ASP.NET necessários ou o número de reciclagens do thread de trabalho nos servidores, o que dispararia eventos no lado do monitoramento de rede.

O dimensionamento do aplicativo ASP.NET não tem impacto sobre o pessoal de desenvolvimento e de rede – assim, vale muito a pena envolver ambos os grupos no processo de decisão. É comum a colaboração produzir soluções originais que as equipes, trabalhando de maneira independente, não descobririam. Por exemplo, o pessoal de rede pode reconhecer soluções de hardware existentes na empresa capazes de ajudar os desenvolvedores a atender aos requisitos de desempenho e de dimensionamento. A conversa sobre os detalhes do aplicativo e do ambiente revelará essas oportunidades.

O combate conjunto a incêndios

Recursos para o dimensionamento de aplicativos ASP.NET

Uma crise no dimensionamento não é necessariamente algo ruim – na verdade, isso costuma ser bom porque vem à tona com muitos usuários que desejam executar o aplicativo, o que comprova o seu valor! Agora, porém, você precisa fazê-lo funcionar.

Às vezes, as crises no dimensionamento acontecem por conta de outro evento – às vezes, a empresa está fazendo uma promoção, você blogou a respeito ou um site de relacionamentos direcionou um grande número de pessoas para o site ao mesmo tempo. De uma hora para outra, você está em meio a uma batalha para manter o aplicativo em funcionamento. Como você deve imaginar, é melhor considerar esse tipo de cenário antes que ele aconteça de fato. E uma demonstração de como a colaboração pode ajudar uma organização a superar esse tipo de crise é uma maneira excelente de encerrar a reunião de desenvolvedores e equipe de rede.

A primeira questão ao administrar a crise é "como detectamos que o site está sendo sobrecarregado?" A resposta costuma ser: "recebemos uma ligação do CTO". Se o primeiro sinal de que o site está com problemas for uma ligação de alguém de fora, você realmente tem um problema. Uma medição mínima deve informar os problemas antes que o telefone toque. O telefone não irá parar de tocar, de fato, mas pelo menos isso dará uma idéia de como atendê-lo. Dizer "ah, sério?" para o CTO não ajuda muito na carreira, não.

Próxima pergunta: para quem ligar primeiro? Quem responde ao evento? Em geral, o pessoal de rede recebe o primeiro aviso e, como esse pessoal pode ser relativamente inexperiente, deve haver um plano de escalonamento claro. Para quem ligar em seguida? O desafio é fazer um diagnóstico rápido, preventivo, para saber que tipo de problema você está enfrentando. Caso seja uma queda na rede, isso é uma coisa. Mas se o problema é de dimensionamento, isso é algo totalmente diferente. Em problemas de dimensionamento, é recomendável chamar logo alguém da equipe de desenvolvimento envolvido.

Como todos os envolvidos no combate ao incêndio precisam de boas informações, direitos de acesso efetivos são essenciais. O objetivo é disseminar o máximo de informações possível para que seja feito um diagnóstico efetivo, e isso deve acabar ditando o escopo da solução.

Normalmente, se a única resposta for escrever um código, o evento terá passado antes que ele escrito. É claro que o evento deve ser observado para poder ajudar a formar prioridades para o desenvolvimento no futuro, mas não é razoável (ou inteligente) tentar escrever muitos códigos e colocá-los em produção antes de um teste cuidadoso. A regra número 1 de qualquer combate a incêndio: jamais coloque mais lenha na fogueira.

Quando surgem os problemas de dimensionamento, às vezes, a solução é simplesmente aguardar. Porém, ainda assim, é preciso tomar uma decisão.

Ao mesmo tempo, o planejamento em relação a essas contingências significa que diferentes técnicas podem ser aplicadas rapidamente. Reunir recursos de rede e de desenvolvimento para resolver um problema de dimensionamento costuma resolvê-lo em um tempo consideravelmente bom, talvez antes mesmo do suficiente para fazer desse evento promocional mais do que um sucesso.

Concluindo, a lição aprendida no lado da rede é que um aplicativo ASP.NET com bom desempenho e dimensionamento representa uma colaboração bem-sucedida entre o pessoal que criou o aplicativo e aqueles que o implantam e o operam.

Como a colaboração é inevitável, é melhor começar logo com as reuniões entre as equipes para compartilhar as informações. O objetivo delas é criar uma compreensão e um consenso de todos os elementos do aplicativo, o que ele faz, como faz, do que depende, como se comporta sob carga e como lida com problemas que surgem com o decorrer do tempo.

Quando essa colaboração é, de fato, efetiva, o resultado é uma empresa mais ágil, capaz de responder rapidamente diante de problemas, além de poder comunicar objetivos claros quanto ao necessário para que o aplicativo tenha êxito no futuro.

Richard Campbell é diretor regional da Microsoft, MVP em ASP.NET e co-apresentador do .NET Rocks, o programa de entrevistas em áudio da Internet destinado a desenvolvedores .NET (visite dotnetrocks.com). Ele trabalhou para empresas durante anos como consultor de desempenho e dimensionamento do ASP.NET, além de ser um dos co-fundadores da Strangeloop Networks.

© 2008 Microsoft Corporation e CMP Media, LLC. Todos os direitos reservados; é proibida a reprodução total ou parcial sem permissão.