4 modi per migliorare le prestazioni di ASP.NET con carichi di picco

 

Introduzione

ASP.NET sta diventando molto popolare per lo sviluppo di applicazioni Web e molte di queste applicazioni sono di natura ad alto traffico e servono milioni di utenti. Di conseguenza, queste applicazioni hanno un grande impatto sul business e sono quindi molto importanti.

È interessante notare che l'architettura dell'applicazione ASP.NET è molto scalabile a livello di applicazione. E anche il protocollo HTTP è senza stato. Entrambi significano che è possibile eseguire l'applicazione ASP.NET in una Web farm con bilanciamento del carico in cui ogni richiesta HTTP viene instradata al server Web più appropriato. Ciò ti consente di aggiungere facilmente più server Web alla tua web farm man mano che il traffico degli utenti aumenta. E questo rende il livello dell'applicazione ASP.NET molto scalabile. Ma cosa intendo per scalabilità qui?

La scalabilità è essenzialmente la capacità di fornire prestazioni elevate anche con carichi di picco. Pertanto, se il tempo di risposta della pagina dell'applicazione ASP.NET è molto veloce con 10 utenti e rimane altrettanto veloce con 100,000 utenti, l'applicazione ASP.NET è scalabile. Tuttavia, se il tempo di risposta ASP.NET rallenta all'aumentare del numero di utenti, l'applicazione non è scalabile.

 

Il problema: colli di bottiglia della scalabilità

Nonostante un'architettura molto scalabile a livello di applicazione, le applicazioni ASP.NET oggi devono affrontare gravi colli di bottiglia in termini di scalabilità. Questi colli di bottiglia si verificano in quattro aree diverse come segue:

  1. Database dell'applicazione
  2. Archiviazione dello stato della sessione ASP.NET
  3. ASP.NET View State
  4. Esecuzione della pagina ASP.NET per output statico

Lascia che ti spieghi ciascuno in modo più dettagliato di seguito.

 

Database dell'applicazione

Il database delle applicazioni come SQL Server o Oracle diventa rapidamente un collo di bottiglia per la scalabilità man mano che aumenti il ​​carico delle transazioni. Ciò accade perché, sebbene sia possibile aumentare il livello di database acquistando un server di database più potente, non è possibile eseguire la scalabilità orizzontale aggiungendo più server al livello di database. Ad esempio, è molto comune vedere 10-20 server a livello di applicazione, ma non è possibile fare lo stesso a livello di database.

 

Archiviazione dello stato della sessione ASP.NET

Inoltre, lo stato della sessione ASP.NET deve essere archiviato da qualche parte. Inoltre, le opzioni predefinite fornite da Microsoft sono InProc, StateServer e SqlServer. Sfortunatamente, tutte e tre le opzioni presentano importanti problemi di prestazioni e scalabilità. InProc e StateServer ti obbligano a utilizzare sessioni permanenti e a inviare tutte le richieste HTTP sullo stesso server in cui è stata creata la sessione. Se si configura StateServer come server autonomo per evitare sessioni persistenti, StateServer diventa un singolo punto di errore e anche le sue prestazioni diventano un problema importante. Inoltre, SqlServer archivia le sessioni ASP.NET nel database di SQL Server come BLOB. E ci sono seri problemi di prestazioni e scalabilità con questo approccio.

 

ASP.NET View State

ASP.NET View State è una stringa nascosta codificata (spesso di 100 KB di dimensione) che viene inviata al browser dell'utente come parte della risposta HTTP. Il browser non fa nulla con esso e lo restituisce al server web in caso di HTTP Post-Back. Ciò rallenta la risposta della pagina ASP.NET, aumenta il carico sulle schede di rete del server Web e consuma anche molta larghezza di banda aggiuntiva. E, come sai, la larghezza di banda non è economica.

 

Esecuzione della pagina ASP.NET per output statico

Infine, l'ASP.NET framework esegue una pagina ASP.NET su una richiesta dell'utente, anche se l'output della pagina non cambia rispetto alla richiesta precedente. Questo può andare bene in ambienti a bassa transazione. Ma in un ambiente con transazioni elevate in cui stai già estendendo tutte le risorse ai loro limiti, questa esecuzione aggiuntiva può diventare piuttosto costosa e un collo di bottiglia per la scalabilità.

Come si suol dire "la forza di ogni catena è forte solo quanto il suo anello più debole". Pertanto, finché sono presenti colli di bottiglia di scalabilità in qualsiasi punto dell'ambiente applicativo ASP.NET, l'intera applicazione rallenta e si ferma persino.

E, ironia della sorte, questo accade nei picchi di carico quando si svolgono i più alti livelli di attività commerciale. Pertanto, l'impatto di qualsiasi rallentamento o fermo macchina è molto più costoso per il tuo business.

ASP.NET affronta i colli di bottiglia della scalabilità
Figura 1: ASP.NET affronta i colli di bottiglia della scalabilità
 

NoSQL Database Non la risposta

NoSQL database movimento è iniziato a causa dei problemi di scalabilità nei database relazionali. NoSQL database partiziona i dati su più server e ti consente di scalare verso l'esterno proprio come il livello dell'applicazione.

Ma, NoSQL database ti richiede di abbandonare il tuo database relazionale e di inserire i tuoi dati in a NoSQL database. E questo è più facile a dirsi che a farsi per una serie di motivi e in effetti non è possibile in molti casi.

NoSQL databases non hanno la stessa capacità di gestione e ricerca dei dati dei database relazionali e desiderano che i dati vengano archiviati in un modo completamente diverso da quello relazionale. Inoltre, l'ecosistema che circonda i database relazionali è troppo forte per essere abbandonato per la maggior parte delle aziende.

Come risultato, NoSQL databases sono utili solo quando hai a che fare con dati non strutturati. Inoltre, la maggior parte delle applicazioni ASP.NET tratta dati aziendali che sono prevalentemente strutturati e adatti ai database relazionali. Di conseguenza, questi dati non possono essere spostati in a NoSQL database facilmente.

E, anche quelli che finiscono per usare a NoSQL database farlo per un piccolo sottoinsieme dei loro dati totali che possono essere considerati non strutturati. E usano NoSQL database insieme al loro database relazionale esistente.

Pertanto, la maggior parte delle volte dovrai convivere con il tuo database relazionale e trovare un'altra soluzione per i tuoi problemi di scalabilità. Fortunatamente, esiste una soluzione molto praticabile e ne parlerò di seguito.

 

La soluzione: cache distribuita in memoria

La soluzione a tutti i problemi sopra menzionati consiste nell'utilizzare la cache distribuita in memoria nella distribuzione dell'applicazione come NCache. NCache offre Cache distribuita open source per .NET che è estremamente veloce e linearmente scalabile. Pensalo come un negozio di oggetti in memoria che viene anche distribuito. Essere in memoria lo rende estremamente veloce ed essere distribuito lo rende linearmente scalabile.

La cosa bella di una cache distribuita in memoria come NCache è che non ti chiede di smettere di usare il tuo database relazionale esistente. È possibile utilizzare la cache sopra il database relazionale perché la cache rimuove tutti i colli di bottiglia della scalabilità del database relazionale.

NCache Fornisce scalabilità lineare
Figura 2: NCache Fornisce scalabilità lineare

Allora, com'è una cache distribuita in memoria NCache più scalabile di un database relazionale? Bene, NCache forma un cluster di server cache e raggruppa insieme la CPU, la memoria e altre risorse da tutti questi server.

E, NCache consente di aggiungere server di cache in fase di esecuzione senza interrompere la cache o l'applicazione. Inoltre, ciò ti consente di scalare linearmente la tua applicazione e gestire carichi di transazioni estremi. Questo è qualcosa che non puoi fare con il tuo database relazionale.

Cache distribuita in memoria come NCache scala in modo lineare consentendo di aggiungere server di cache al cluster di cache (il livello di memorizzazione nella cache) in fase di esecuzione. Ma che tipo di numeri di prestazioni dovresti aspettarti da una soluzione come NCache.

Qui di seguito sono NCache numeri di prestazione Puoi vedere tutto NCache benchmark delle prestazioni

Numeri di prestazione per NCache
Figura 3: Numeri di prestazioni per NCache

Come puoi vedere, una cache distribuita in memoria piace NCache fornisce prestazioni inferiori al millisecondo per letture e scritture e consente di scalare la capacità delle transazioni in modo lineare semplicemente aggiungendo più server cache.

Vediamo ora come piace a una cache distribuita in memoria NCache risolve vari colli di bottiglia di scalabilità sopra menzionati.

 

Memorizzazione nella cache dei dati dell'applicazione

La memorizzazione nella cache dei dati dell'applicazione consente di rimuovere i colli di bottiglia del database. Cache distribuita in memoria come NCache consente di memorizzare nella cache i dati dell'applicazione e ridurre i costosi viaggi del database. Puoi aspettarti di deviare il 70-90% del traffico del database alla cache distribuita in memoria. Ciò riduce la pressione sul database e gli consente di funzionare più velocemente e di gestire carichi di transazioni più grandi senza rallentamenti.

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: utilizzo della cache distribuita in memoria per la memorizzazione nella cache dei dati delle app

La memorizzazione nella cache dei dati dell'applicazione significa memorizzare nella cache tutti i dati dell'applicazione ottenuti dal database relazionale. Questo di solito è sotto forma di oggetti di dominio (chiamati anche entità). Ecco un esempio su come utilizzare una cache distribuita come NCache per la memorizzazione nella cache dei dati dell'applicazione.

 

Memorizzazione nella cache dello stato della sessione ASP.NET

Cache distribuita in memoria come NCache è anche un ottimo posto per archiviare lo stato della sessione ASP.NET. È molto più veloce e scalabile di tutte e tre le opzioni sopra menzionate (InProc, StateServer e SqlServer). NCache è più veloce perché è in memoria e fornisce un'interfaccia valore-chiave con il valore che è un "oggetto" che è uno stato di sessione ASP.NET. Ed è scalabile perché è una cache distribuita.

E, NCache replica anche in modo intelligente le sessioni attraverso le sue ricche topologie di memorizzazione nella cache, quindi anche se un server cache si interrompe, non si verifica alcuna perdita di dati della sessione. Questa replica è necessaria perché NCache fornisce un archivio in memoria e la memoria viola l'archiviazione.

NCache accelera inoltre la serializzazione dell'oggetto Session State ASP.NET necessario prima che possa essere archiviato fuori processo. NCache lo fa usando la sua funzionalità di serializzazione compatta dinamica che è 10 volte più veloce della normale serializzazione .NET. È possibile utilizzare questa funzione senza apportare modifiche al codice.

Puoi collegarti NCache Fornitore dello stato della sessione (SSP) all'applicazione ASP.NET apportando alcune modifiche al file web.config come mostrato di seguito.

<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 come ASP.NET Session State Provider (SSP) in Web.Config

 

ASP.NET View State Caching

Ho già descritto come ASP.NET View State è una stringa codificata inviata dal server web al browser dell'utente che poi la restituisce al server web in caso di HTTP Post Back. Ma, con l'aiuto di una cache distribuita InMemory come NCache, puoi memorizzarlo nella cache ASP.NET View State sul server e inviare solo un piccolo ID univoco al suo posto.

NCache ha implementato un ASP.NET View State modulo di memorizzazione nella cache tramite un adattatore di pagina ASP.NET personalizzato e ASP.NET PageStatePersister. Per di qua, NCache intercetta sia la richiesta che la risposta HTTP. Al momento della risposta, NCache rimuove la parte "valore" della stringa codificata e la memorizza nella cache e inserisce invece un identificatore univoco (una chiave cache) in questo "valore".

Quindi, quando arriva la successiva richiesta HTTP, la intercetta di nuovo e sostituisce l'identificatore univoco con la stringa codificata effettiva che ha inserito nella cache in precedenza. In questo modo, la pagina ASP.NET non nota nulla di diverso e utilizza la stringa codificata contenente lo stato di visualizzazione come prima.

L'esempio seguente mostra un ASP.NET View State stringa codificata senza memorizzazione nella cache e anche cosa succede quando viene incorporata la memorizzazione nella cache.

//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 Stringa codificata con o senza memorizzazione nella cache

 

Cache di output ASP.NET per output di pagine statiche

ASP.NET fornisce un ASP.NET Output Cache Framework per risolvere il problema dell'esecuzione eccessiva della pagina anche quando l'output della pagina non cambia. Questo framework ti consente di memorizzare nella cache l'output dell'intera pagina o di alcune parti della pagina in modo che la prossima volta che questa pagina verrà chiamata, non verrà eseguita e verrà invece visualizzato il suo output memorizzato nella cache. La visualizzazione di un output già memorizzato nella cache è molto più veloce che eseguire nuovamente l'intera pagina.

<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: configurazione del provider di cache di output ASP.NET per NCache in web.config

NCache ha implementato un provider di cache di output ASP.NET per .NET 4.0 o versioni successive. Ciò ti consente di collegarti NCache senza interruzioni e senza alcuno sforzo di programmazione. In caso di NCache, questo provider è per una cache distribuita in memoria che si estende su più server. Pertanto, se l'applicazione ASP.NET è in esecuzione in una Web farm con bilanciamento del carico, l'output della pagina memorizzato nella cache dal server 1 è immediatamente disponibile per tutti gli altri server nella Web farm. Di seguito è riportato come è possibile collegare NCache come provider di cache di output ASP.NET.

 

Architettura della cache distribuita

Le applicazioni ASP.NET ad alto traffico non possono permettersi di scendere soprattutto durante le ore di punta. Per questi tipi di applicazioni, ci sono tre importanti obiettivi architetturali che piacciono a una buona cache distribuita in memoria NCache fornisce. Sono:

  1. Alta disponibilità
  2. Scalabilità lineare
  3. Replicazione e affidabilità dei dati

Lascia che ti spieghi ogni area di seguito.

 

Alta disponibilità

Uno degli obiettivi architettonici più importanti di NCache è quello di ottenere un'elevata disponibilità e l'elasticità della cache. E lo fa attraverso le seguenti capacità architettoniche:

  1. Cluster di cache peer-to-peer con riparazione automatica: NCache crea un cluster di server cache su TCP/IP. Questo cluster ha a peer-to-peer architettura che significa che non ci sono nodi master/slave e nessun clustering con regole di maggioranza. Invece, ogni nodo è un peer uguale. Ciò consente NCache per gestire situazioni in cui qualsiasi nodo potrebbe interrompersi e il cluster si adatta automaticamente e continua a funzionare e non si verificano interruzioni per l'applicazione.
  2. Configurazione dinamica: Ciò significa che non è necessario codificare le cose nei file di configurazione. Questo è perché NCache propaga molte informazioni di configurazione per memorizzare nella cache i client (ovvero le tue applicazioni) in fase di esecuzione. Pertanto, quando si aggiunge un server di cache in fase di esecuzione, l'appartenenza al cluster viene aggiornata automaticamente e il cluster informa tutti i client di cache di questa modifica. Esistono numerose altre modifiche alla configurazione che vengono gestite allo stesso modo.
  3. Supporto per il failover della connessione: Questa è una funzionalità in cui quando un server della cache si interrompe, il cluster della cache ei client della cache sono in grado di continuare a funzionare senza alcuna interruzione. In caso di cluster di cache, ho già discusso della sua qualità di autoriparazione che risolve questa situazione. In caso di client cache, ciò significa che il client cache continua a funzionare interagendo con altri server cache nel cluster.
 

Replica dei dati con scalabilità lineare

Dal momento che la cache distribuita in memoria piace NCache utilizza la memoria come archivio, deve fornire la replica dei dati per garantire l'affidabilità. Ma, allo stesso tempo, non può compromettere la scalabilità lineare perché questo è il motivo più importante per utilizzare una cache distribuita come NCache.

Qui ci sono alcuni NCache Topologie di memorizzazione nella cache che aiutano a raggiungere entrambi questi obiettivi.

  1. Cache partizionata: NCache partiziona la cache in base al numero di server cache e assegna una partizione a ciascun server cache. Regola anche il numero di partizioni quando aggiungi o rimuovi server cache in fase di esecuzione. Il partizionamento è il modo principale per garantire la scalabilità lineare perché quando si aggiungono più server, questa topologia di memorizzazione nella cache aumenta la dimensione complessiva dello storage e anche la potenza di elaborazione della CPU.
  2. Cache della replica partizionata: Oltre al partizionamento, NCache fornisce anche repliche per ogni partizione. Queste repliche risiedono su server cache diversi rispetto alla partizione stessa per garantire che se un server cache si interrompe insieme alla relativa partizione, la replica diventi immediatamente disponibile. In questo modo viene garantita l'affidabilità dei dati. Replicando ogni partizione solo una volta su un altro server cache, NCache raggiunge l'affidabilità dei dati senza compromettere la scalabilità lineare.
  3. Cache client (vicino alla cache): Un'altra capacità molto importante di NCache è la cache del cliente. Questa è una cache locale che si trova sulla macchina client della cache (ovvero il tuo server Web o app) e può anche essere InProc (il che significa che risiede all'interno del processo dell'applicazione). Si tratta essenzialmente di una cache in cima a una cache e offre miglioramenti delle prestazioni estremi insieme a una maggiore scalabilità di NCache stesso perché il traffico anche al livello di memorizzazione nella cache diminuisce.
Topologia di memorizzazione nella cache di partizione-replica di NCache
Figura 8: Topologia di memorizzazione nella cache di partizione-replica di NCache

Come puoi vedere, Partitioned-Replica Cache inserisce una partizione e una replica su ciascun server cache. Inoltre, garantisce che la replica sia sempre su un server cache diverso per motivi di affidabilità.

 

Conclusione

Ho cercato di evidenziare i colli di bottiglia più comuni in termini di prestazioni e scalabilità che le applicazioni ASP.NET devono affrontare oggi e mostrarti come superarli utilizzando una cache distribuita in memoria come NCache. NCache è una cache distribuita Open Source per applicazioni .NET e Java. Quindi, puoi usarlo senza alcuna restrizione. Puoi saperne di più su NCache al seguente link

Cosa fare dopo?

© Copyright Alachisoft 2002 - . Tutti i diritti riservati. NCache è un marchio registrato di Diyatech Corp.