In questo articolo, esamineremo come una soluzione di memorizzazione nella cache distribuita può migliorare drasticamente le prestazioni complessive e il throughput dell'applicazione basata su microservizi.
In una tipica applicazione basata su microservizi, più microservizi lavorano insieme pur rimanendo liberamente accoppiati e scalabili. L'applicazione dispone di servizi necessari per soddisfare i requisiti aziendali fondamentali, come il monitoraggio e l'elaborazione dei dati aziendali critici. Esistono anche microservizi dedicati aggiuntivi che gestiscono l'identità e l'autenticazione, il monitoraggio dell'integrità e del carico, oltre a fungere da gateway API.
Una caratteristica chiave di un'applicazione di questo tipo è che ogni microservizio può essere progettato, sviluppato e distribuito in modo indipendente utilizzando qualsiasi stack tecnologico desiderato. Poiché ogni microservizio è un'app autonoma a sé stante, mantiene anche il proprio archivio persistente separato, sia esso un database relazionale, un NoSQL DB o anche un sistema di archiviazione di file legacy. Ciò consente ai singoli microservizi di scalare in modo indipendente e rende le modifiche dell'infrastruttura in tempo reale molto più gestibili.
NCache Dettagli Microservizi con NCache Ridimensiona Pub/Sub nei microservizi
Perché il tuo microservizio ha bisogno NCache?
Ci sono casi in cui si verificano ancora colli di bottiglia durante l'aumento delle transazioni sull'applicazione. Questo è prevalentemente comune nelle architetture in cui i microservizi archiviano i dati in database relazionali che non consentono la scalabilità orizzontale. In tali situazioni, la scalabilità orizzontale del microservizio distribuendo più istanze non risolve il problema.
Per contrastare questi problemi, puoi introdurre senza problemi NCache come cache distribuita a livello di caching tra i microservizi e i datastore. NCache aiuta anche come broker di messaggistica publisher/subscriber scalabile in memoria per consentire comunicazioni asincrone tra microservizi.
Scalabilità tramite Pub/Sub
La comunicazione dei microservizi viene spesso implementata utilizzando il modello Publisher/Subscriber che consente la messaggistica tra i microservizi mantenendoli liberamente accoppiati. A tal proposito, NCache funge da broker di messaggistica Pub/Sub scalabile in memoria attraverso il quale tutti i microservizi che compongono l'applicazione possono pubblicare e sottoscrivere eventi. La caratteristica di scalabilità e affidabilità inerente a NCache i clustering vengono tradotti automaticamente quando arriviamo a pub/sub. Scopri di più su NCache come broker di messaggi nell'ambiente dei microservizi attraverso il nostro blog su Ridimensionamento della comunicazione dei microservizi .NET con Pub/Sub in memoria.
NCache Dettagli Pub/Sub Messaggistica in NCache Ridimensiona Pub/Sub nei microservizi
Scalabilità attraverso la memorizzazione nella cache
NCache offre scalabilità in tempo reale, consentendoti di aggiungere tutti i nodi del server che desideri nel tuo cluster di cache in esecuzione senza incorrere in tempi di inattività dell'applicazione. Usando NCache non solo migliora le prestazioni complessive dei singoli microservizi agendo come un veloce archivio in memoria, ma facilita anche un notevole aumento dei tempi di risposta e della disponibilità delle applicazioni attraverso la sua architettura di clustering. Ciò è particolarmente vero quando si considerano i flussi di lavoro che coinvolgono dozzine di microservizi distribuiti su più host.
Istruzioni per l’uso: NCache per la memorizzazione nella cache dei dati?
Con NCache invece, se un microservizio richiede dati, controlla prima la cache invece di accedere direttamente al database ogni volta. Dato che i dati a cui si accede più di frequente di solito costituiscono una piccola parte dell'intero dato disponibile nel datastore, avere tali dati già memorizzati nella cache e disponibili per l'uso riduce notevolmente la latenza relativa al database e facilita il carico sul database poiché la maggior parte delle richieste di dati sono servite dalla cache stessa.
Visto che un'applicazione basata su microservizi è intrinsecamente più lenta rispetto a quando viene creata utilizzando una struttura di progettazione monolitica, è chiaro che è necessario il guadagno di velocità offerto da NCache. NCache, a livello di microservizio, può aiutare a sfruttare la potenza di un'architettura di microservizi riducendo al contempo la latenza complessiva osservata durante lunghe transazioni che si estendono su più servizi che lavorano in sequenza.
NCache ha diverse funzionalità pronte all'uso che forniscono un controllo granulare sulle operazioni di memorizzazione nella cache. Queste operazioni includono l'applicazione della coerenza della cache utilizzando la scadenza e la sincronizzazione del database, ricche API che aiutano a implementare funzionalità cache-aside e cache-through utilizzando provider di origine di supporto. NCache fornisce anche operazioni di query simili a SQL sulla cache usando la query SQL, oltre a fungere da provider di memorizzazione nella cache per Object Relational Mapper (ORM) come EF Core.
Per iniziare NCache nella tua applicazione basata su microservizi, la prima cosa di cui hai bisogno è configurare i servizi. Ciò fornisce le informazioni richieste che i tuoi microservizi devono iniziare a utilizzare NCache. Una panoramica di come è possibile creare un contesto reciproco tra NCache e la tua applicazione basata su microservizi è mostrata di seguito:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
public IServiceProvider ConfigureServices(IServiceCollection services) { //Add additional code here services.AddDbContext<CatalogContext>(options => { var cacheID = configuration["CatalogCache"]; if (string.IsNullOrEmpty(cacheID)) cacheID = "CatalogCache"; NCacheConfiguration.Configure(cacheID, DependencyType.Other); // Changing default behavior when client evaluation occurs to throw. // Default in EF Core would be to log a warning when client evaluation is performed. options.ConfigureWarnings(warnings => warnings.Throw(RelationalEventId.QueryClientEvaluationWarning)); //Check Client vs. Server evaluation: https://docs.microsoft.com/en-us/ef/core/querying/client-eval }); var container = new ContainerBuilder(); container.Populate(services); return new AutofacServiceProvider(container.Build()); } |
Quello che devi fare ora è distribuire un controller con la logica che gli consenta di ottenere un elemento dalla cache se lo trova lì. In caso contrario, il controller recupera l'elemento dal database e lo memorizza nella cache. L'implementazione di tale controller è mostrata di seguito:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
[Route("api/v1/[controller]")] [ApiController] public class CatalogController : ControllerBase { private readonly CatalogContext _catalogContext; private readonly CatalogSettings _settings; private readonly ICatalogIntegrationEventService _catalogIntegrationEventService; public CatalogController(CatalogContext context, IOptionsSnapshot<CatalogSettings> settings, ICatalogIntegrationEventService catalogIntegrationEventService) { _catalogContext = context ?? throw new ArgumentNullException(nameof(context)); _catalogIntegrationEventService = catalogIntegrationEventService ?? throw new ArgumentNullException(nameof(catalogIntegrationEventService)); _settings = settings.Value; } [HttpGet] [Route("items/{id:int}")] [ProducesResponseType((int)HttpStatusCode.NotFound)] [ProducesResponseType((int)HttpStatusCode.BadRequest)] [ProducesResponseType(typeof(CatalogItem), (int)HttpStatusCode.OK)] public async Task<ActionResult<CatalogItem>> ItemByIdAsync(int id) { if (id <= 0) { return BadRequest(); } CatalogItem item = null; var cache = _catalogContext.GetCache(); string catalogItemKey = "CatalogItem:" + id; //Getting item from cache item = cache.Get<CatalogItem>(catalogItemKey); if (item == null) { item = await _catalogContext.CatalogItems.SingleOrDefaultAsync(ci => ci.Id == id); cache.Insert(catalogItemKey, item); } // Your logic here if (item != null) return item; return NotFound(); } } |
NCache Dettagli Microservizi con NCache
Esaminiamo i loro dettagli per conoscere il vero fascino di NCache nei microservizi.
Mantieni la cache fresca, sempre
C'è un avvertimento importante nell'uso di una cache che è correlata alla cache che conserva i dati "non aggiornati" rispetto al contenuto del datastore primario sottostante. Per assicurarti che un determinato microservizio riceva dati aggiornati dalla sua cache, è necessario aggiornare regolarmente i dati della cache. Fortunatamente, NCache fornisce funzionalità come Sincronizzazione database ed Scadenza per garantire che i dati rimangano coerenti con quelli nell'archivio dati principale.
È possibile mantenere un livello di sincronizzazione della cache con il datastore semplicemente aggiungendo un intervallo di tempo di scadenza agli elementi memorizzati nella cache. Una volta scaduto, NCache rimuove l'elemento memorizzato nella cache in modo che le richieste successive per le stesse informazioni comportino la memorizzazione nella cache dei dati aggiornati. NCache offre entrambi Assoluta così come Scorrevole strategie di scadenza ed è possibile utilizzarle a seconda della natura transitoria dei dati in questione.
Ad esempio, il seguente frammento di codice mostra con quanta facilità puoi introdurre la scadenza assoluta su un particolare elemento della cache:
1 2 3 4 |
var cacheItem = new CacheItem(product); var expiration = new Expiration(ExpirationType.Absolute, TimeSpan.FromMinutes(5)); cacheItem.Expiration = expiration; cache.Insert(key, cacheItem); |
Per utilizzare la scadenza scorrevole, tutto ciò che devi fare è modificare ExpirationType come tale:
1 |
var expiration = new Expiration(ExpirationType.Sliding, TimeSpan.FromMinutes(5)); |
Un requisito importante quando si impostano le scadenze in qualsiasi cache per mantenere la coerenza dei dati della cache è che i tempi di scadenza impostati devono essere in base alla velocità con cui la parte specifica di dati sta cambiando sul lato dell'archivio dati. Se si impostano tempi di scadenza troppo brevi, i dati potrebbero essere rimossi inutilmente e si tradurrà in un viaggio del datastore non necessario e costoso; se il tempo di scadenza è troppo lungo, potrebbero essere utilizzati dati obsoleti.
NCache Dettagli Scadenza dei dati in NCache
Trovare i valori ottimali per i tempi di scadenza, quindi, richiede una profonda conoscenza dei modelli di cambiamento dello stato dei dati che di solito non è fattibile. Quando i requisiti di coerenza della cache diventano più rigorosi, le strategie di sincronizzazione del database sono l'approccio consigliato. NCache fornisce diverse strategie di sincronizzazione del database a questo proposito.
Utilizzando questi, è possibile sincronizzare la cache con il datastore senza dover accedere ai modelli di accesso ai dati di ciascuna informazione come richiesto quando si utilizza la scadenza. Ora, ogni volta che viene apportata una modifica a quell'elemento sul lato dell'archivio dati, la cache può rimuovere automaticamente quell'elemento senza ulteriori ritardi.
Per vederlo in azione, il frammento di codice seguente mostra come eseguire la sincronizzazione NCache con un database SQL Server aggiungendo NCache Dipendenza SQL sugli elementi memorizzati nella cache.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
// Creating SQL Dependency string query = "SELECT ProductName, UnitPrice FROM dbo.Products WHERE CategoryID = 'Dairy';"; SqlCacheDependency sqlDependency = new SqlCacheDependency(connectionString, query); // Get orders that contain products with given category ID Order[] orders = FetchOrdersByProductCategoryID("Dairy"); foreach (var order in orders) { // Generate a unique cache key for this order string key = $"Order:ProductCategory-Dairy:{order.OrderID}"; // Create a new cacheitem and add sql dependency to it CacheItem item = new CacheItem(order); item.Dependency = sqlDependency; //Add cache item in the cache with SQL Dependency cache.Insert(key, item); } |
Query SQL sulla cache
NCache offre ai tuoi microservizi la possibilità di eseguire query sui dati della cache indicizzata tramite un meccanismo di query simile a SQL. Questa caratteristica si rivela preziosa nei casi in cui i valori delle chiavi rispetto alle quali sono memorizzate le informazioni richieste sono sconosciuti. Ciò estrae anche gran parte delle chiamate API della cache di livello inferiore e rende il codice dell'applicazione abbastanza più facile da comprendere e mantenere. Questa funzione si rivela particolarmente adatta a te se ti senti più a tuo agio con i comandi simili a SQL.
Un frammento di codice di esempio che dimostra l'uso di NCache La funzionalità di query SQL è fornita di seguito:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
string query = "SELECT * FROM FQN.Product WHERE ProductID > ?"; // Use QueryCommand for query execution var queryCommand = new QueryCommand(query); // Providing parameters for query queryCommand.Parameters.Add("ProductID",50000); // Executing QueryCommand through ICacheReader ICacheReader reader = cache.SearchService.ExecuteReader(queryCommand); // Check if the result set is not empty if (reader.FieldCount > 0) { while (reader.Read()) { string result = reader.GetValue<string>(1); // Perform operations using the retrieved keys } } else { // Null query result set retrieved } |
Le query SQL possono funzionare con indici di query, il NCache strutture dati distribuite e tag della cache. Segui il link per maggiori informazioni su come utilizzare il NCache Funzionalità query SQL.
NCache Dettagli Query SQL in NCache
Read-Thru e Write-Thru
Usando il NCache Fornitori di origini dati caratteristica, set NCache come unico ingresso nel livello di accesso ai dati dal punto di vista del microservizio; se un microservizio richiede dati, deve solo accedere alla cache. La cache quindi fornisce i dati se sono disponibili nella cache, ma in caso contrario, va avanti e recupera i dati dal datastore usando un gestore di lettura per conto del client, li memorizza nella cache e li presenta al microservizio.
Allo stesso modo, utilizzando un gestore write-thru, un microservizio deve solo eseguire un'operazione di scrittura (Aggiungi, Aggiorna, Elimina) sulla cache e la cache esegue automaticamente l'operazione di scrittura pertinente sull'archivio dati.
Inoltre, puoi persino forzare la cache a recuperare i dati direttamente dall'archivio dati, indipendentemente dal fatto che la cache ne contenga una versione eventualmente obsoleta. Questo è fondamentale quando il microservizio richiede informazioni aggiornate e si basa sulle strategie di coerenza della cache menzionate in precedenza.
La funzione di supporto del provider di origine dati non solo semplifica il codice dell'applicazione, ma se utilizzata insieme a molti altri NCache funzionalità di sincronizzazione del database disponibili, la cache viene conservata con dati aggiornati ricaricati automaticamente pronti per il calcolo.
Il seguente frammento di codice ti aiuterà a iniziare a utilizzare Read-Thru nei tuoi microservizi:
1 2 3 4 5 6 |
// Specify the readThruOptions for read through operations var readThruOptions = new ReadThruOptions(); readThruOptions.Mode = ReadMode.ReadThru; // Retrieve the data of the corresponding item with reads thru enabled Product data = cache.Get<Product>(key, readThruOptions); |
Allo stesso modo, puoi implementare il write-through usando:
1 2 3 4 5 6 |
// Enable write through for the cacheItem created var writeThruOptions = new WriteThruOptions(); writeThruOptions.Mode = WriteMode.WriteBehind; // Add item in the cache with write-behind cache.Insert(key, cacheItem, writeThruOptions); |
Per informazioni più dettagliate su come utilizzare questi provider, fare riferimento alla nostra documentazione all'indirizzo Memorizzazione nella cache di lettura ed Memorizzazione nella cache di scrittura.
NCache Dettagli Fornitori di origini dati in NCache
Memorizzazione nella cache di base di EF
Nucleo dell'Entity Framework (EF). è un potente Object Relational Mapper (O/RM) usato frequentemente nelle applicazioni .NET aziendali. E poiché è così popolare, NCache offre una Provider di memorizzazione nella cache di EF Core che ti consente di aggiungere facilmente la memorizzazione nella cache all'interno del codice correlato a EF Core usando metodi di estensione come FromCache. Ciò consente agli sviluppatori di EF Core che non hanno familiarità con NCache API di cui avvalersi ancora della potenza NCache.
Il codice seguente dimostra la facilità d'uso di NCache Provider di memorizzazione nella cache di EF Core per introdurre la memorizzazione nella cache nella logica dell'applicazione del microservizio esistente.
1 2 3 4 5 6 7 8 9 |
var options = new CachingOptions { // To store the result as collection in cache StoreAs = StoreAs.Collection }; options.SetAbsoluteExpiration(DateTime.Now.AddMinutes(_settings.NCacheAbsoluteExpirationTime)); // Get items from cache. If not found, fetch from database and store in cache. item = await _catalogContext.CatalogItems.DeferredSingleOrDefault(ci => ci.Id == id).FromCacheAsync(options); |
Puoi trovare ulteriori informazioni sull'API EF Core Caching Provider e su come può aiutare il tuo business case a NCache Fornitore principale di EF.
NCache Dettagli Memorizzazione nella cache di EF Core NCache
Riassumendo tutto
I microservizi sono costruiti con l'intento specifico di essere autonomi; che puoi svilupparli, testarli e distribuirli indipendentemente dagli altri microservizi. Ciò serve a rendere l'intera applicazione altamente scalabile oltre ad essere aperta a processi di integrazione continua/distribuzione continua (CI/CD) veloci.
Tuttavia, nonostante tutti i vantaggi offerti dai microservizi in termini di scalabilità e cicli di vita di sviluppo rapidi, ci sono alcuni aspetti di uno stack di applicazioni che causano intoppi. Tra questi aspetti ci sono i database relazionali che non consentono la scalabilità orizzontale necessaria per far fronte all'aumento del carico ed è qui che una soluzione di memorizzazione nella cache distribuita come NCache brilla.
NCache ha numerose funzionalità pronte all'uso per aiutarti a rendere la memorizzazione nella cache dei dati un'aggiunta indolore e intuitiva alla tua applicazione di microservizi. Questi includono sincronizzazione del database, scadenza, memorizzazione nella cache di EF Core, query SQL e molto di piu.