.NET Core e ASP.NET Core stanno guadagnando popolarità grazie alla loro semplicità di progettazione, alla leggerezza, al fatto di essere open source e di poter funzionare sia su Windows che su Linux. Di conseguenza, molte applicazioni esistenti stanno migrando da .NET Framework a .NET Core. Quasi tutte le nuove applicazioni vengono sviluppate in .NET Core.
Molte di queste applicazioni .NET Core sono caratterizzate da un elevato volume di traffico, servendo milioni di utenti e transazioni. Di conseguenza, queste applicazioni hanno un impatto enorme sulla tua attività e sono quindi di fondamentale importanza.
Le applicazioni .NET Core che in genere necessitano di scalabilità sono applicazioni server che devono elaborare un gran numero di transazioni molto rapidamente, con tempi di risposta altrettanto rapidi. Molte di queste applicazioni sono rivolte direttamente ai clienti, ovvero elaborano le loro richieste. Se non riescono a evadere le richieste dei clienti in tempi brevi, il costo per l'azienda è elevato in termini di mancati ricavi e perdita di clienti soddisfatti.
Le seguenti applicazioni .NET Core richiedono scalabilità:
È interessante notare che tutte le applicazioni sopra menzionate hanno architetture a livello di applicazione molto scalabili. Ognuno di essi ti consente di scalare linearmente all'aumentare del carico delle transazioni aggiungendo più server, macchine virtuali o istanze di container insieme a un sistema di bilanciamento del carico.
Nonostante un'architettura altamente scalabile a livello applicativo, le applicazioni server .NET Core oggi si trovano ad affrontare importanti colli di bottiglia in termini di scalabilità. Questi colli di bottiglia si verificano in diverse aree, come ad esempio:
Il principale collo di bottiglia per tutte le applicazioni .NET Core ad alto traffico è il database dell'applicazione. La maggior parte delle applicazioni odierne utilizza ancora un database relazionale come SQL Server o Oracle. Questi database diventano rapidamente un collo di bottiglia in termini di scalabilità all'aumentare del carico di transazioni sulle applicazioni. Questo vale sia che si utilizzi SQL Server su una macchina virtuale sia Azure SQL Database.
Questo accade perché un database relazionale non può essere partizionato logicamente come un database NoSQL e rimane invece in un'unica posizione fisica; anche un partizionamento a livello di colonna non è minimamente paragonabile a un vero partizionamento in stile NoSQL. Pertanto, non è possibile aumentare la capacità di transazione del livello del database aggiungendo altri server di database, come si può fare con un database NoSQL.
Ad esempio, mentre il livello dell'applicazione può facilmente avere 10, 20, 30 o più server delle applicazioni all'aumentare del carico delle transazioni, il livello del database non può crescere nello stesso modo.
Per questo motivo, il tuo database relazionale diventa un collo di bottiglia delle prestazioni per tutti i dati in esso archiviati (dati dell'applicazione o altri dati).
SQL Server ha introdotto ottimizzazioni in memoria per aumentare il numero di transazioni al secondo. Oracle ha anche fornito la propria versione delle tabelle In-Memory.
Sebbene le ottimizzazioni in memoria portino a miglioramenti delle prestazioni, non affrontano il problema principale della scalabilità lineare. Le tabelle in memoria vengono generalmente utilizzate per dati di sola lettura e per scalare una capacità di transazione di sola lettura, è necessario aggiungere più istanze di SQL Server su computer di fascia alta.
Le tabelle in memoria hanno anche limitazioni sulla dimensione dei dati; non puoi mettere in memoria tabelle di grandi dimensioni poiché l'intera tabella deve essere messa in memoria. E la loro replica su altre istanze di SQL Server può essere eseguita solo su altre tabelle in memoria e non su un database corretto.
In sintesi, queste ottimizzazioni in memoria nei database SQL Server e Oracle non sono in grado di soddisfare appieno le esigenze di scalabilità della tua applicazione .NET Core.
Uno dei motivi per cui i database NoSQL sono diventati popolari è la loro capacità di partizionare correttamente i dati in base ad algoritmi di hash e ad altri metodi. Questo risolve molti dei problemi di scalabilità relativi alla capacità di gestione delle transazioni che affliggono i database relazionali come SQL Server e Oracle.
Tuttavia, esistono dei motivi per cui i database NoSQL non rappresentano la soluzione ideale per questi colli di bottiglia dei database.
La soluzione a tutti i problemi sopra menzionati è utilizzare una cache distribuita in memoria simile NCache nella distribuzione della tua applicazione .NET Core. NCache è una cache distribuita open source per .NET e .NET Core estremamente veloce e con scalabilità lineare. Immaginatela come un archivio dati in memoria distribuito. Essendo in memoria, è estremamente veloce, mentre la distribuzione ne garantisce la scalabilità lineare.
NCache è linearmente scalabile perché crea un cluster TCP di server cache a basso costo (stessa configurazione dei server delle app Web ma con più memoria) e raggruppa la memoria e le risorse della CPU di tutti questi server in un'unica capacità logica. NCache quindi ti consente di aggiungere server di cache a questo cluster in fase di esecuzione all'aumentare del carico delle transazioni. E, da allora NCache È interamente in memoria, è velocissimo e offre tempi di risposta inferiori al millisecondo, cosa che non ci si può aspettare dai database relazionali o persino dai database NoSQL.
Oltre a fornire scalabilità lineare, una cache distribuita come NCache replica i dati in modo intelligente in modo che le tue prestazioni non vengano compromesse, garantendo al contempo l'affidabilità dei dati in caso di guasto di un server cache.
NCache consente di scalare le applicazioni .NET Core tramite le seguenti funzionalità:
Il collo di bottiglia più importante affrontato dalle applicazioni .NET Core è il "database dell'applicazione". La cosa bella di NCache è che, a differenza dei database NoSQL, NCache non ti chiede di smettere di usare il tuo database relazionale esistente. Puoi continuare a utilizzare SQL Server, database SQL di Azure, Oracle e così via come database e ottenere comunque una scalabilità lineare utilizzando NCache in cima al tuo database relazionale. Questo è perché NCache rimuove tutti i colli di bottiglia della scalabilità del database relazionale perché, a differenza del tuo database, NCache è in realtà linearmente scalabile.
La memorizzazione nella cache dei dati dell'applicazione consente di rimuovere i colli di bottiglia del database. NCache consente di memorizzare nella cache i dati dell'applicazione e ridurre i costosi viaggi del database. Puoi aspettarti di deviare l'80-90% del traffico del database verso NCache. Ciò riduce la pressione sul database e gli consente di funzionare più velocemente e di gestire carichi di transazioni più grandi senza rallentamenti.
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 di come utilizzare una cache distribuita come NCache per la memorizzazione nella cache dei dati dell'applicazione.
Customer Load(string custId)
{
ICache cache = CacheManager.GetCache("myCache");
string key = "Customer:CustomerID:" + custId;
Customer cust = cache.Get<Customer>(key);
if (cust == null)
{
// Item not in cache so load from db
LoadCustomerFromDb(cust);
// Add item to cache for future reference
cache.Add(key, cust);
}
return cust;
}
Figura 3: utilizzo della cache distribuita in memoria per la memorizzazione nella cache dei dati delle app
Un altro possibile collo di bottiglia si presenta se si memorizzano le sessioni ASP.NET Core in SQL Server o in MemoryCache autonomo. Entrambe le opzioni presentano notevoli limitazioni in termini di prestazioni e scalabilità. L'archiviazione su SQL Server non è adatta alle sessioni ASP.NET Core e diventa rapidamente un collo di bottiglia, proprio come accade per i dati dell'applicazione.
NCache è un ottimo posto dove archiviare le sessioni ASP.NET Core perché è molto più veloce e scalabile rispetto ad altre opzioni di archiviazione. NCache È più veloce perché è in memoria e fornisce un'interfaccia chiave-valore in cui il valore è un "oggetto", quale è una Sessione ASP.NET Core. Inoltre, è scalabile perché è una cache distribuita.
E, NCache replica inoltre in modo intelligente le sessioni ASP.NET Core attraverso le sue ricche topologie di caching, in modo che anche se un server di cache si arresta, non si verifichi alcuna perdita di dati di sessione. Questa replica è necessaria perché NCache fornisce un archivio in memoria e la memoria viola l'archiviazione.
NCache Inoltre, velocizza la serializzazione della sessione ASP.NET Core, necessaria prima che possa essere memorizzata fuori dal processo. NCache Questo risultato viene ottenuto grazie alla funzionalità di serializzazione dinamica compatta, che è 10 volte più veloce della serializzazione standard di .NET e .NET Core. È possibile utilizzare questa funzionalità senza apportare alcuna modifica al codice.
Puoi usare NCache come archivio di sessioni ASP.NET Core in due modi.
Di seguito è riportato un esempio di come è possibile configurare l'applicazione ASP.NET Core per utilizzare NCache Fornitore della sessione:
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
// Specify NCache as the session provider
services.AddNCacheSession(Configuration.GetSection("NCacheSettings"));
...
}
public void Configure(IApplicationBuilder app, ...)
{
// select NCache session provider for ASP.NET Core
app.UseNCacheSession();
...
}
}
Figura 4: plug-in NCache come sessioni ASP.NET Core
Le applicazioni ASP.NET Core, che altrimenti presentano contenuti piuttosto dinamici, si trovano ad affrontare situazioni in cui, per alcune pagine, il contenuto o la risposta non cambiano tra una richiesta e l'altra. Tuttavia, queste pagine devono essere eseguite ogni volta che arriva una richiesta. Ciò comporta un carico eccessivo sulle risorse del server web e su tutti i livelli dell'applicazione. Di conseguenza, si creano colli di bottiglia nelle prestazioni e si limita la scalabilità dell'applicazione.
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
// Turn on ASP.NET Core Response Cache with IDistributedCache
services.AddResponseCaching();
// Select NCache as IDistributedCache provider
services.AddNCacheDistributedCache(Configuration.GetSection("NCacheSettings"));
...
}
}
Figura 5: plug-in NCache come middleware di cache di risposta ASP.NET Core
Per affrontare il sovraccarico dell'esecuzione ripetitiva della pagina quando la risposta della pagina non cambia, ASP.NET Core ha fornito un meccanismo di caching della risposta della pagina chiamato ASP.NET Response Cache Middleware. E, NCache ha implementato l'interfaccia IDistributedCache in ASP.NET Core grazie alla quale è possibile collegarla senza problemi NCache come middleware di cache di risposta per ASP.NET Core.
Quindi, puoi usare NCache per memorizzare nella cache le risposte delle pagine ASP.NET Core per un certo periodo di tempo, in modo che la prossima volta che la stessa pagina viene chiamata con gli stessi parametri, questa risposta memorizzata nella cache possa essere restituita invece di eseguire nuovamente l'intera pagina. Di seguito è riportato un esempio di codice su come configurare NCache come middleware di cache di risposta per ASP.NET Core.
Se la tua applicazione ASP.NET Core è un'applicazione web in tempo reale, molto probabilmente utilizza ASP.NET Core SignalR per fornire questo comportamento in tempo reale. Le applicazioni web in tempo reale forniscono aggiornamenti ad alta frequenza dal server al client. Esempi di tali applicazioni includono giochi, aste, votazioni, social network, ecc.
Se la tua applicazione ASP.NET Core è in esecuzione in un ambiente multi-server con bilanciamento del carico, deve utilizzare un provider SignalR Backplane per ASP.NET Core per condividere gli eventi tra più server web. Inoltre, questo Backplane deve essere scalabile. In caso contrario, la tua applicazione SignalR per ASP.NET Core inizierà a presentare colli di bottiglia nelle prestazioni.
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
// Specify NCache as the ASP.NET Core SignalR Backplane
services.AddSignalR().AddNCache(ncacheOptions =>
{ ncacheOptions.CacheName = "myPartitionedCache"; });
...
}
public void Configure(IApplicationBuilder app, ...)
{
// Use SignalR in ASP.NET Core
app.UseSignalR(config => { config.MapHub<MessageHub>("/messages"); });
...
}
}
Figura 6: plug-in NCache come provider di backplane SignalR per ASP.NET Core
NCache ha implementato un provider di backplane SignalR per ASP.NET Core. NCacheIl provider di backplane SignalR di ASP.NET Core utilizza le funzionalità di messaggistica Pub/Sub di NCache che sono estremamente veloci grazie al fatto di essere completamente in memoria. Ciò consente alla tua applicazione ASP.NET Core SignalR di accelerare la propagazione degli eventi SignalR tra tutti i server web e, di conseguenza, verso i client.
E questo rende la tua applicazione web in tempo reale più reattiva nel fornire quegli aggiornamenti frequenti ai clienti. Inoltre, puoi continuare ad aumentare il numero di client e anche aggiungere più server Web senza temere colli di bottiglia delle prestazioni.
Se la tua applicazione .NET Core deve utilizzare la messaggistica Pub/Sub o gli eventi, è molto probabile che utilizzi una piattaforma di messaggistica Pub/Sub non completamente in memoria, ma che memorizza tutti i messaggi su disco. Di conseguenza, questo può facilmente diventare un collo di bottiglia per le prestazioni se la tua applicazione gestisce un numero elevato di transazioni.
NCache fornisce anche una messaggistica Pub/Sub che è super veloce perché è completamente in memoria. E replica tutti i messaggi su un altro NCache server per garantire che non si tratti di una perdita di dati in caso di inattività di un server.
Pertanto, se la tua applicazione .NET Core utilizza NCache come piattaforma di messaggistica Pub/Sub, sperimenterà prestazioni super veloci e scalabilità lineare perché NCache di per sé è linearmente scalabile.
Di seguito è riportato un esempio di come utilizzare Pub/Sub Messaging fornito da NCache nella tua applicazione .NET Core.
private void PublishMessage (string topicName)
{
ITopic topic = _cache.MessagingService.GetTopic(topicName);
Order order = Order.GenerateOrder<Order>();
// Publish message containing "order" with expiry
Message message = new Message(order, new TimeSpan(0, 0, 15));
topic.Publish(message, DeliveryOption.All, true);
}
private ITopicSubscription SubscribeMessage (string topicName)
{
ITopic topic = _cache.MessagingService.GetTopic(topicName);
// Subscribes to the topic. Message delivered to MessageReceivedCallback
return topic.CreateSubscription(MessageReceivedCallback);
}
static void MessageReceivedCallback(object sender, MessageEventArgs args) { ... }
Figura 7: Utilizzo della messaggistica Pub/Sub nelle app .NET Core
Il principale collo di bottiglia in termini di scalabilità che la tua applicazione .NET Core deve eliminare è rappresentato dal database dell'applicazione. In quest'area, le tue applicazioni possono raggiungere prestazioni elevate e scalabilità lineare grazie alla memorizzazione nella cache dei dati. Il motivo è semplice: la maggior parte delle applicazioni .NET Core gestisce un'enorme quantità di dati in entrata e in uscita dal database.
Quando si tratta di memorizzazione nella cache dei dati delle applicazioni, la più grande paura che le persone hanno è che la cache diventi obsoleta, il che significa che contiene una versione precedente dei dati che è già stata modificata nel database da un altro utente o da un'altra applicazione.
Questa paura che una cache diventi obsoleta è così forte che la maggior parte delle persone memorizza nella cache solo dati statici o di sola lettura (dati di riferimento). Tuttavia, questi dati di sola lettura rappresentano solo il 20% dei dati totali sotto forma di tabelle di ricerca e altri dati di riferimento. La maggior parte dei dati nel database è transazionale, inclusi clienti, account, attività, ecc. E, se non si memorizzano nella cache questi dati transazionali, non si beneficia completamente della memorizzazione nella cache.
Quindi, il vero vantaggio della memorizzazione nella cache viene se puoi memorizzare nella cache tutti i tipi di dati senza il timore che la memorizzazione nella cache diventi obsoleta. NCache fornisce una serie di funzionalità per affrontare questo problema.
Il modo più efficace per mantenere aggiornata la cache è mantenerla sempre sincronizzata con il database. NCache ti consente di farlo su una varietà di database come segue:
Quando sincronizzi la tua cache con SQL Server, chiedi NCache per registrarsi come client di SQL Server e quindi eseguire una chiamata SqlDependency insieme a un set di dati basato su query SQL. Quindi, quando SQL Server rileva eventuali modifiche in questo set di dati, invia una notifica NCache a proposito
private static void CreateSqlDependency (Product product)
{
string connectionString = "Data Source=localhost;Database=northwind;...";
// SQL stmt on which the SQL Dependency is created in SQL Server
string sqlStmt = "SELECT ProductID, ProductName, QuantityPerUnit, UnitPrice " +
"FROM dbo.PRODUCTS WHERE ProductID = " + product.Id;
CacheDependency sqlDependency = new SqlCacheDependency(connectionString, sqlStmt);
CacheItem cacheItem = new CacheItem(product) { Dependency = sqlDependency };
string key = "Product:ProductId:" + product.Id; ;
cache.Add(key, cacheItem);
}
Figura 8: utilizzo di SqlDependency per sincronizzare la cache con SQL Server
Poi, NCache rimuove questo elemento dalla cache, quindi la prossima volta che l'applicazione ne avrà bisogno, dovrà recuperare l'ultima copia dal database. Se stai usando il gestore Read-through (vedi sotto), allora NCache può anche ricaricare automaticamente l'ultima copia dal database per te. Di seguito è riportato un esempio di come utilizzare SqlDependency per sincronizzare la cache con SQL Server.
La cache di lettura è una cache in grado di leggere i dati dal database chiamando un gestore di lettura che hai sviluppato e fornito alla cache. Allo stesso modo, una cache di scrittura è in grado di scrivere le modifiche ai dati nel database chiamando un gestore di scrittura che hai sviluppato e fornito alla cache. La cache write-behind è la stessa di quella write-through, tranne per il fatto che gli aggiornamenti del database vengono eseguiti in modo asincrono.
Le funzionalità di lettura diretta (Read-through), scrittura diretta (Write-through) e scrittura differita (Write-behind) offrono numerosi vantaggi alle applicazioni .NET Core, tra cui:
Di seguito è riportato un esempio di come utilizzare Read-through con NCache.
// Read through handler for SQL Server
public class SqlReadThruProvider : Runtime.DatasourceProviders.IReadThruProvider
{
public void Init(IDictionary parameters, string cacheId) {}
public void Dispose() {}
// Get object from the database/data-source based on the key
public ProviderCacheItem LoadFromSource(string key) {}
// Bulk-Get objects from the database/data-source based on the keys
public IDictionary<string, ProviderCacheItem> LoadFromSource(ICollection<string> keys)
{}
}
Figura 9: utilizzo di Read-through Handler con NCache
Una volta che sei a tuo agio nella memorizzazione nella cache di tutti i dati, puoi iniziare a inserire molti dati in una cache distribuita. Qui puoi iniziare ad affrontare un altro problema peculiare di come trovare rapidamente e facilmente i tuoi dati. Poiché la maggior parte delle cache distribuite sono archivi di valori-chiave, diventa molto difficile tenere traccia di tutti i dati solo tramite le chiavi.
Qui è dove NCache ti offre una varietà di modi per trovare rapidamente i dati dalla tua cache. Esempi inclusi:
Di seguito è riportato un esempio di come utilizzare le query basate su LINQ con NCache.
// Search the cache based on object attributes by using LINQ
IQueryable>Product< products = new NCacheQuery<Product>(_cache);
var result = from product in products
where product.Id > 10
select product;
if (result != null)
{
foreach (Product p in result1)
{
// Process each “product” fetched from the database
Console.WriteLine("ProductID : " + p.Id);
}
}
Figura 10: utilizzo di query LINQ con NCache
Le applicazioni .NET Core ad alto traffico non possono permettersi di andare in crash, soprattutto durante le ore di punta. Per questo tipo di applicazioni, ci sono tre obiettivi architetturali davvero importanti che una buona cache distribuita in memoria come NCache soddisfa.
Lascia che ti spieghi ciascuno di seguito.
NCache fornisce una cache client che è una cache locale molto vicina alla tua applicazione. Può essere InProc (il che significa che risiede all'interno del processo di applicazione) o OutProc locale. In ogni caso, fornisce un accesso molto rapido a un sottoinsieme dei dati memorizzati nella cache di cui la tua applicazione su questo server app ha bisogno in questo momento. Allo stesso tempo, la cache del client rimane sincronizzata con il livello di memorizzazione nella cache, quindi tutti i dati modificati da altri utenti o applicazioni nel livello di memorizzazione nella cache vengono immediatamente propagati alla cache del client. Il client ti consente di avere la velocità di InProc pur facendo parte di un livello di memorizzazione nella cache molto scalabile.
Uno degli obiettivi architettonici più importanti di NCache è ottenere una scalabilità lineare con affidabilità dei dati attraverso le sue topologie di memorizzazione nella cache. Eccotene alcune NCache Topologie di memorizzazione nella cache che aiutano a raggiungere entrambi questi obiettivi.
Uno degli obiettivi architettonici più importanti di NCache è quello di ottenere un'elevata disponibilità e l'elasticità della cache. Lo fa attraverso le seguenti capacità architettoniche:
© Copyright Alachisoft 2002 - . Tutti i diritti riservati. NCache è un marchio registrato di Diyatech Corp.