4 maneiras de melhorar o desempenho do ASP.NET sob cargas de pico

 

Introdução

O ASP.NET está se tornando muito popular para o desenvolvimento de aplicativos da Web e muitos desses aplicativos são de alto tráfego por natureza e atendem a milhões de usuários. Como resultado, esses aplicativos têm um grande impacto nos negócios e, portanto, são muito importantes.

Curiosamente, a arquitetura do aplicativo ASP.NET é muito escalável na camada do aplicativo. E o protocolo HTTP também é sem estado. Ambos significam que você pode executar seu aplicativo ASP.NET em um web farm com balanceamento de carga em que cada solicitação HTTP é roteada para o servidor web mais apropriado. Isso permite que você adicione facilmente mais servidores da Web ao seu web farm à medida que o tráfego de usuários aumenta. E isso torna sua camada de aplicativo ASP.NET muito escalável. Mas o que quero dizer com escalabilidade aqui?

A escalabilidade é essencialmente a capacidade de fornecer alto desempenho mesmo sob cargas de pico. Portanto, se o tempo de resposta da página do aplicativo ASP.NET for muito rápido com 10 usuários e permanecer tão rápido com 100,000 usuários, seu aplicativo ASP.NET será escalável. Mas, se o tempo de resposta do ASP.NET diminuir à medida que você aumenta o número de usuários, seu aplicativo não é escalável.

 

O problema: gargalos de escalabilidade

Apesar de uma arquitetura muito escalável na camada de aplicativos, os aplicativos ASP.NET hoje enfrentam grandes gargalos de escalabilidade. Esses gargalos estão ocorrendo em quatro áreas diferentes:

  1. Banco de dados de aplicativos
  2. Armazenamento de estado de sessão ASP.NET
  3. ASP.NET View State
  4. Execução de página ASP.NET para saída estática

Deixe-me explicar cada um com mais detalhes abaixo.

 

Banco de dados de aplicativos

Banco de dados de aplicativos como SQL Server ou Oracle rapidamente se torna um gargalo de escalabilidade à medida que você aumenta a carga de transações. Isso acontece porque, embora você possa dimensionar a camada de banco de dados comprando um servidor de banco de dados mais poderoso, não é possível dimensionar adicionando mais servidores à camada de banco de dados. Por exemplo, é muito comum ver de 10 a 20 servidores na camada do aplicativo, mas você não pode fazer o mesmo na camada do banco de dados.

 

Armazenamento de estado de sessão ASP.NET

Além disso, o estado de sessão do ASP.NET precisa ser armazenado em algum lugar. E as opções prontas fornecidas pela Microsoft são InProc, StateServer e SqlServer. Infelizmente, todas as três opções têm grandes problemas de desempenho e escalabilidade. InProc e StateServer forçam você a usar sessões fixas e enviar todas as solicitações HTTP no mesmo servidor em que a sessão foi criada. Se você configurar o StateServer como um servidor autônomo para evitar sessões fixas, o StateServer se tornará um ponto único de falha e seu desempenho também se tornará um grande problema. E o SqlServer armazena sessões ASP.NET no banco de dados SQL Server como BLOBs. E há sérios problemas de desempenho e escalabilidade com essa abordagem.

 

ASP.NET View State

ASP.NET View State é uma string oculta codificada (geralmente 100 KB de tamanho) que é enviada ao navegador do usuário como parte da resposta HTTP. O navegador não faz nada com ele e o retorna ao servidor web no caso de um HTTP Post-Back. Isso diminui a resposta da página ASP.NET, sobrecarrega as placas de rede do servidor Web e também consome muita largura de banda extra. E, como você sabe, a largura de banda não é barata.

 

Execução de página ASP.NET para saída estática

Por fim, ASP.NET framework executa uma página ASP.NET em uma solicitação do usuário, mesmo que a saída da página não seja alterada em relação à solicitação anterior. Isso pode ser bom em ambientes de baixa transação. Mas em um ambiente de alta transação onde você já está levando todos os recursos ao limite, essa execução extra pode se tornar bastante cara e um gargalo de escalabilidade.

Como diz o ditado “a força de qualquer corrente é tão forte quanto seu elo mais fraco”. Portanto, enquanto houver gargalos de escalabilidade em qualquer lugar no ambiente de aplicativo ASP.NET, todo o aplicativo ficará mais lento e até mesmo parará.

E, ironicamente, isso acontece sob cargas de pico quando você está realizando os níveis mais altos de atividade comercial. Portanto, o impacto de qualquer desaceleração ou tempo de inatividade é muito mais caro para o seu negócio.

ASP.NET enfrentando gargalos de escalabilidade
Figura 1: ASP.NET enfrentando gargalos de escalabilidade
 

NoSQL Database Não é a resposta

NoSQL database movimento começou como resultado dos problemas de escalabilidade em bancos de dados relacionais. NoSQL database particiona os dados em vários servidores e permite que você escale horizontalmente como a camada do aplicativo.

Mas, NoSQL database exige que você abandone seu banco de dados relacional e coloque seus dados em um NoSQL database. E, isso é mais fácil dizer do que fazer por uma série de razões e, de fato, não é possível em muitos casos.

NoSQL databases não têm o mesmo gerenciamento de dados e capacidade de pesquisa que os bancos de dados relacionais e desejam que você armazene dados de uma maneira totalmente diferente da relacional. Além disso, o ecossistema em torno dos bancos de dados relacionais é muito forte para ser abandonado pela maioria das empresas.

Como resultado, NoSQL databases são úteis apenas quando você está lidando com dados não estruturados. E a maioria dos aplicativos ASP.NET está lidando com dados de negócios que são predominantemente estruturados e adequados para bancos de dados relacionais. Como resultado, esses dados não podem ser movidos para um NoSQL database facilmente.

E, mesmo aqueles que acabam usando um NoSQL database fazê-lo para um pequeno subconjunto de seus dados totais que podem ser considerados não estruturados. E, eles usam NoSQL database junto com seu banco de dados relacional existente.

Portanto, na maioria das vezes você precisará conviver com seu banco de dados relacional e encontrar outra solução para seus problemas de escalabilidade. Felizmente, existe uma solução muito viável e discutirei isso abaixo.

 

A solução: cache distribuído na memória

A solução para todos os problemas mencionados acima é usar o Cache Distribuído In-Memory na implantação do seu aplicativo, como NCache. NCache é um Cache distribuído de código aberto para .NET que é extremamente rápido e linearmente escalável. Pense nisso como um armazenamento de objetos na memória que também é distribuído. Estar na memória o torna extremamente rápido e ser distribuído o torna linearmente escalável.

O bom de um cache distribuído na memória como NCache é que ele não pede que você pare de usar seu banco de dados relacional existente. Você pode usar o cache sobre seu banco de dados relacional porque o cache remove todos os gargalos de escalabilidade do banco de dados relacional.

NCache Fornece escalabilidade linear
Figura 2: NCache Fornece escalabilidade linear

Então, como é um cache distribuído na memória NCache mais escalável do que um banco de dados relacional? Nós vamos, NCache forma um cluster de servidores de cache e agrupa a CPU, memória e outros recursos de todos esses servidores.

E, NCache permite adicionar servidores de cache em tempo de execução sem interromper o cache ou o aplicativo. E isso permite dimensionar linearmente seu aplicativo e lidar com cargas de transações extremas. Isso é algo que você não pode fazer com seu banco de dados relacional.

Cache distribuído na memória como NCache escala linearmente, permitindo que você adicione servidores de cache ao cluster de cache (a camada de cache) em tempo de execução. Mas, que tipo de números de desempenho você deve esperar de uma solução como NCache.

A seguir estão listadas NCache números de desempenho. Você pode ver completo NCache benchmark de desempenho Aqui.

Números de desempenho para NCache
Figura 3: Números de desempenho para NCache

Como você pode ver, um cache distribuído na memória como NCache fornece desempenho abaixo de milissegundos para leituras e gravações e permite que você dimensione sua capacidade de transação linearmente simplesmente adicionando mais servidores de cache.

Vamos agora ver como um Cache Distribuído In-Memory como NCache resolve vários gargalos de escalabilidade mencionados acima.

 

Cache de dados do aplicativo

O Application Data Caching permite que você remova os gargalos do banco de dados. Cache distribuído na memória como NCache permite armazenar em cache os dados do aplicativo e reduzir essas viagens caras ao banco de dados. Você pode esperar desviar de 70 a 90% do tráfego do banco de dados para o Cache Distribuído na Memória. Isso reduz a pressão em seu banco de dados e permite que ele tenha um desempenho mais rápido e lide com cargas de transações maiores sem diminuir a velocidade.

Customer Load(string customerId)
{
 // Key format: Customer:PK:1000
 string key = "Customers:CustomerID:" + customerId;
 Customer cust = (Customer) _cache[key];
 if (cust == null)
 { // Item not in cache so load from db
LoadCustomerFromDb(cust);
// Add item to cache for future reference
_cache.Insert(key, cust);
 }
 return cust;
}

Figura 4: como usar o cache distribuído na memória para o cache de dados do aplicativo

O cache de dados de aplicativos significa que você armazena em cache quaisquer dados de aplicativos obtidos de seu banco de dados relacional. Isso geralmente está na forma de objetos de domínio (também chamados de entidades). Aqui está um exemplo de como usar um cache distribuído como NCache para cache de dados do aplicativo.

 

Cache de estado de sessão ASP.NET

Cache distribuído na memória como NCache também é um ótimo lugar para armazenar seu estado de sessão ASP.NET. É muito mais rápido e escalável do que todas as três opções mencionadas acima (InProc, StateServer e SqlServer). NCache é mais rápido porque está na memória e fornece uma interface de valor-chave com o valor sendo um "objeto" que é um estado de sessão ASP.NET. E é escalável porque é um cache distribuído.

E, NCache também replica de forma inteligente as sessões por meio de suas ricas topologias de cache, de modo que, mesmo que um servidor de cache fique inativo, não há perda de dados da sessão. Essa replicação é necessária porque NCache fornece um armazenamento na memória e a memória viola o armazenamento.

NCache também acelera a serialização do objeto ASP.NET Session State que é necessário antes que ele possa ser armazenado fora do processo. NCache faz isso usando seu recurso Dynamic Compact Serialization que é 10 vezes mais rápido que a serialização .NET normal. Você pode usar esse recurso sem fazer nenhuma alteração no código.

Você pode conectar NCache Provedor de estado de sessão (SSP) para seu aplicativo ASP.NET fazendo algumas alterações em seu arquivo web.config conforme mostrado abaixo.

<system.web>
 ...
 <assemblies>
<add assembly="Alachisoft.NCache.SessionStoreProvider, Version=4.3.0.0,
 Culture=neutral, PublicKeyToken=CFF5926ED6A53769" />
 </assemblies>
 <sessionState cookieless="false" regenerateExpiredSessionId="true"
 mode="Custom" customProvider="NCacheSessionProvider"
timeout="20">
<providers>
 <add name="NCacheSessionProvider"
 type="Alachisoft.NCache.Web.SessionState.NSessionStoreProvider"
 useInProc="false" cacheName="myDistributedCache"
 enableLogs="false“ writeExceptionsToEventLog="false” />
</providers>
 </sessionState>
 ...
</system.web>

Figura 5: Plug-in NCache como ASP.NET Session State Provider (SSP) em Web.Config

 

ASP.NET View State Cache

Já descrevi como ASP.NET View State é uma string codificada enviada pelo servidor web para o navegador do usuário que então a retorna ao servidor web no caso de um HTTP Post Back. Mas, com a ajuda de um Cache Distribuído InMemory como NCache, você pode armazenar isso em cache ASP.NET View State no servidor e enviar apenas um pequeno ID exclusivo no lugar dele.

NCache implementou um ASP.NET View State módulo de cache por meio de um Adaptador de Página ASP.NET personalizado e ASP.NET PageStatePersister. Deste jeito, NCache intercepta a solicitação e a resposta HTTP. No tempo de resposta, NCache remove a parte "valor" da string codificada e a armazena em cache e, em vez disso, coloca um identificador exclusivo (uma chave de cache) nesse "valor".

Então, quando a próxima solicitação HTTP chegar, ele a interceptará novamente e substituirá o identificador exclusivo pela string codificada real que ele colocou no cache anteriormente. Dessa forma, a página ASP.NET não percebe nada diferente e usa a string codificada que contém o estado de exibição da maneira como fazia antes.

O exemplo abaixo mostra um ASP.NET View State string codificada sem cache e também o que acontece quando o cache é incorporado.

//ASP.NET View State without Caching
<input id="__VIEWSTATE"
 	type="hidden"
 	name="__VIEWSTATE"
 	value="/wEPDwUJNzg0MDMxMDA1D2QWAmYPZBYCZg9kFgQCAQ9kFgICBQ9kFgJmD2QWAgIBD
		xYCHhNQcm2aW91c0NvbnRyb2xNb2RlCymIAU1pY3Jvc29mdC5TaGFyZVBvaW50Lld
		lYkNvbnRyb2xzLlNQQ29udHJbE1vZDA1XzRlMjJfODM3Y19kOWQ1ZTc2YmY1M2IPD
		xYCHhNQcm2aW91c0NvbnRyb2xNb2RlCymIAU1pY3Jvc29mdC5TaGFyZVBvaW50Lld
		lYkNvbnRyb2xzLlNQQ29udHJbE1vZDA1XzRlMjJfODM3Y19kOWQ1ZTc2YmY1M2IPD
		... ==" />
			
//ASP.NET View State with Caching
<input id="__VIEWSTATE"
	type="hidden"
	name="__VIEWSTATE"
	value="vs:cf8c8d3927ad4c1a84da7f891bb89185" />

Figura 6: ASP.NET View State String codificada com ou sem cache

 

Cache de saída ASP.NET para saída de página estática

O ASP.NET fornece um ASP.NET Output Cache Framework para resolver o problema de execução excessiva da página, mesmo quando a saída da página não é alterada. Essa estrutura permite armazenar em cache a saída de toda a página ou de algumas partes da página para que, na próxima vez que essa página for chamada, ela não seja executada e, em vez disso, sua saída em cache seja exibida. Exibir uma saída já armazenada em cache é muito mais rápido do que executar a página inteira novamente.

<caching>
   <outputCache defaultProvider ="NOutputCacheProvider">
     <providers>
       <add name="NOutputCacheProvider"
         type="Alachisoft.NCache.OutputCacheProvider.NOutputCacheProvider,
               Alachisoft.NCache.OutputCacheProvider, Version=x.x.x.x,
               Culture=neutral, PublicKeyToken=1448e8d1123e9096"
				  
               cacheName="myDistributedCache" exceptionsEnabled="false"
               writeExceptionsToEventLog="false" enableLogs="true” />"
     </providers>
   </outputCache>
</caching>

Figura 7: Configurando o provedor de cache de saída ASP.NET para NCache em Web.Config

NCache implementou um provedor de cache de saída ASP.NET para .NET 4.0 ou versões posteriores. Isso permite que você conecte NCache perfeitamente e sem qualquer esforço de programação. No caso de NCache, esse provedor é para um Cache Distribuído na Memória que abrange vários servidores. Portanto, se seu aplicativo ASP.NET estiver sendo executado em um web farm com balanceamento de carga, a saída da página armazenada em cache do servidor 1 estará imediatamente disponível para todos os outros servidores no web farm. Abaixo está como você pode conectar NCache como provedor de cache de saída ASP.NET.

 

Arquitetura de cache distribuído

Aplicativos ASP.NET de alto tráfego não podem se dar ao luxo de ficar inativos, especialmente durante os horários de pico. Para esses tipos de aplicativos, existem três objetivos arquiteturais importantes que um bom Cache Distribuído In-Memory como NCache fornece. Eles estão:

  1. Alta disponibilidade
  2. Escalabilidade linear
  3. Replicação e confiabilidade de dados

Deixe-me explicar cada área abaixo.

 

High Availability

Um dos objetivos arquitetônicos mais importantes da NCache é alcançar alta disponibilidade e elasticidade de cache. E faz isso por meio dos seguintes recursos de arquitetura:

  1. Cluster de cache ponto a ponto com autorrecuperação: NCache constrói um cluster de servidores de cache sobre TCP/IP. Este aglomerado tem um peer-to-peer arquitetura que significa que não há nós mestre/escravo e nenhum cluster de regra de maioria. Em vez disso, cada nó é um par igual. Isso permite NCache para lidar com situações em que qualquer nó pode ficar inativo e o cluster se ajusta automaticamente e continua em execução, e não há interrupção para seu aplicativo.
  2. Configuração dinâmica: Isso significa que você não precisa codificar coisas em arquivos de configuração. Isto é porque NCache propaga muitas informações de configuração para clientes de cache (ou seja, seus aplicativos) em tempo de execução. Portanto, quando você adiciona um servidor de cache em tempo de execução, a associação do cluster é atualizada automaticamente e o cluster informa a todos os clientes de cache sobre essa alteração. Há uma série de outras alterações de configuração que são tratadas da mesma forma.
  3. Suporte a failover de conexão: Esse é um recurso no qual quando um servidor de cache fica inativo, o cluster de cache e os clientes de cache podem continuar trabalhando sem nenhuma interrupção. No caso do cluster de cache, já discuti sua qualidade de autorrecuperação que aborda essa situação. No caso de clientes de cache, isso significa que o cliente de cache continua trabalhando interagindo com outros servidores de cache no cluster.
 

Replicação de dados com escalabilidade linear

Como o cache distribuído na memória, como NCache usa a memória como armazenamento, ele deve fornecer replicação de dados para garantir a confiabilidade. Mas, ao mesmo tempo, não pode comprometer a escalabilidade linear porque essa é a razão mais importante para usar um cache distribuído como NCache.

Aqui estão alguns NCache Topologias de cache que ajudam a alcançar esses dois objetivos.

  1. Cache Particionado: NCache particiona o cache com base no número de servidores de cache e atribui uma partição a cada servidor de cache. Ele também ajusta o número de partições quando você adiciona ou remove servidores de cache em tempo de execução. O particionamento é a principal maneira de garantir a escalabilidade linear porque, à medida que você adiciona mais servidores, essa topologia de armazenamento em cache aumenta o tamanho geral do armazenamento e também o poder de processamento da CPU.
  2. Cache de réplica particionada: Além do particionamento, NCache também fornece réplicas para cada partição. Essas réplicas residem em servidores de cache diferentes da própria partição para garantir que, se um servidor de cache ficar inativo junto com sua partição, a réplica ficará imediatamente disponível. Desta forma, a confiabilidade dos dados é fornecida. Ao replicar cada partição apenas uma vez em outro servidor de cache, NCache atinge a confiabilidade dos dados sem comprometer a escalabilidade linear.
  3. Cache do cliente (próximo ao cache): Outra capacidade muito importante de NCache é Cache do Cliente. Este é um cache local que fica na máquina cliente de cache (ou seja, seu servidor web ou de aplicativos) e pode até ser InProc (o que significa que reside no processo do aplicativo). Este é essencialmente um cache em cima de um cache e fornece ganhos de desempenho extremos juntamente com o aumento da escalabilidade de NCache em si mesmo porque o tráfego até mesmo para a camada de cache diminui.
Topologia de cache de réplica de partição de NCache
Figura 8: Topologia de cache de réplica de partição de NCache

Como você pode ver, o Partitioned-Replica Cache coloca uma partição e uma réplica em cada servidor de cache. E garante que a réplica esteja sempre em um servidor de cache diferente para fins de confiabilidade.

 

Conclusão

Tentei destacar os gargalos de desempenho e escalabilidade mais comuns que os aplicativos ASP.NET enfrentam hoje e mostrar como superá-los usando um cache distribuído na memória como NCache. NCache é um cache distribuído de código aberto para aplicativos .NET e Java. Assim, você pode usá-lo sem quaisquer restrições. Você pode saber mais sobre NCache no seguinte link

O que fazer a seguir?

© Copyright Alachisoft 2002 - . Todos os direitos reservados. NCache é uma marca registrada da Diyatech Corp.