Dans cet article, nous verrons comment une solution de mise en cache distribuée peut considérablement améliorer les performances globales et le débit de votre application basée sur des microservices.
Dans une application typique basée sur des microservices, plusieurs microservices fonctionnent ensemble tout en restant faiblement couplés et évolutifs. L'application dispose de services qui sont nécessaires pour répondre aux exigences de base de l'entreprise, telles que le suivi et le traitement des données commerciales critiques. Il existe également des microservices dédiés supplémentaires qui gèrent l'identité et l'authentification, la surveillance de la santé et de la charge, ainsi que des passerelles API.
Une caractéristique clé d'une telle application est que chaque microservice est conçu, développé et déployé indépendamment à l'aide de la pile technologique de votre choix. Étant donné que chaque microservice est une application autonome autonome à part entière, il conserve également son stockage persistant séparé, qu'il s'agisse d'une base de données relationnelle, d'un NoSQL DB ou même un système de stockage de fichiers hérité. Cela permet aux microservices individuels d'évoluer indépendamment et rend les changements d'infrastructure en temps réel beaucoup plus faciles à gérer.
NCache Détails Microservices avec NCache Mettre à l'échelle Pub/Sub dans les microservices
Pourquoi votre microservice a-t-il besoin NCache?
Il existe des cas où des goulots d'étranglement surviennent encore lors de l'augmentation des transactions sur l'application. Ceci est principalement courant dans les architectures où les microservices stockent les données dans des bases de données relationnelles qui ne permettent pas la mise à l'échelle. Dans de telles situations, la mise à l'échelle du microservice en déployant plusieurs instances de celui-ci ne résout pas le problème.
Pour contrer ces problèmes, vous pouvez introduire de manière transparente NCache en tant que cache distribué au niveau de la couche de mise en cache entre vos microservices et les datastores. NCache aide également en tant que courtier de messagerie éditeur/abonné évolutif en mémoire pour permettre des communications asynchrones entre les microservices.
Évolutivité via Pub/Sub
La communication des microservices est fréquemment mise en œuvre à l'aide du modèle Éditeur/Abonné qui permet la messagerie entre les microservices tout en les maintenant faiblement couplés. À cet égard, NCache sert de courtier de messagerie Pub/Sub évolutif en mémoire grâce auquel tous les microservices composant l'application peuvent publier et s'abonner à des événements. L'évolutivité et la fiabilité inhérentes à NCache clustering sont automatiquement traduits lorsque nous arrivons à pub/sub. En savoir plus sur NCache en tant que courtier de messages dans l'environnement des microservices via notre blog sur Mise à l'échelle de la communication des microservices .NET avec Pub/Sub en mémoire.
NCache Détails Messagerie Pub/Sub dans NCache Mettre à l'échelle Pub/Sub dans les microservices
Évolutivité grâce à la mise en cache
NCache offre une évolutivité en temps réel, vous permettant d'ajouter autant de nœuds de serveur que vous le souhaitez dans votre cluster de cache en cours d'exécution sans entraîner de temps d'arrêt de l'application. En utilisant NCache améliore non seulement les performances globales des microservices individuels en agissant comme un magasin en mémoire rapide, mais facilite également une augmentation marquée du temps de réponse et de la disponibilité des applications grâce à son architecture de clustering. Cela est particulièrement vrai lorsque l'on considère des flux de travail impliquant des dizaines de microservices répartis sur plusieurs hôtes.
UTILISATION NCache pour la mise en cache des données ?
Avec NCache en place, si un microservice nécessite des données, il vérifie d'abord le cache au lieu d'accéder directement à la base de données à chaque fois. Étant donné que les données les plus fréquemment consultées constituent généralement une petite partie de l'ensemble des données disponibles dans le magasin de données, le fait que ces données soient déjà mises en cache et disponibles pour utilisation réduit considérablement la latence liée à la base de données et facilite la charge sur la base de données puisque la plupart des demandes de données sont servies par le cache lui-même.
Étant donné qu'une application basée sur des microservices est intrinsèquement plus lente que lorsqu'elle est construite à l'aide d'une structure de conception monolithique, il est clair que vous avez besoin du gain de vitesse offert par NCache. NCache, au niveau des microservices, peut aider à tirer parti de la puissance d'une architecture de microservices tout en réduisant considérablement la latence globale observée lors de longues transactions couvrant plusieurs services fonctionnant de manière séquentielle.
NCache dispose de plusieurs fonctionnalités prêtes à l'emploi qui offrent un contrôle précis sur les opérations de mise en cache. Ces opérations incluent l'application de la cohérence du cache à l'aide de l'expiration et de la synchronisation de la base de données, des API riches qui aident à implémenter des fonctionnalités de mise en cache et de passage en cache à l'aide de fournisseurs de source de sauvegarde. NCache fournit également des opérations de requête de type SQL sur le cache à l'aide d'une requête SQL et sert de fournisseurs de mise en cache pour les mappeurs relationnels d'objets (ORM) tels que EF Core.
Pour commencer NCache dans votre application basée sur les microservices, la première chose dont vous avez besoin est de configurer les services. Cela fournit les informations requises dont vos microservices ont besoin pour commencer à utiliser NCache. Un aperçu de la façon dont vous pouvez créer un contexte mutuel entre NCache et votre application basée sur des microservices est illustrée ci-dessous :
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()); } |
Ce que vous devez faire maintenant est de déployer un contrôleur avec la logique qui lui permet d'obtenir un élément du cache s'il le trouve là-bas. Et si ce n'est pas le cas, le contrôleur récupère l'élément de la base de données et le stocke dans le cache. La mise en œuvre d'un tel contrôleur est illustrée ci-dessous :
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 Détails Microservices avec NCache
Passons en revue leurs détails pour nous familiariser avec les vrais charmes de NCache dans les microservices.
Gardez le cache frais, toujours
Il existe une mise en garde importante concernant l'utilisation d'un cache lié au fait que le cache conserve les données « obsolètes » par rapport au contenu du magasin de données principal sous-jacent. Pour vous assurer qu'un microservice donné reçoit de nouvelles données de son cache, vous devez mettre à jour régulièrement les données du cache. Heureusement, NCache fournit des fonctionnalités telles que Synchronisation De La Base De Données ainsi que Expiration pour s'assurer que les données restent cohérentes avec celles du magasin de données principal.
Vous pouvez maintenir un niveau de synchronisation du cache avec le magasin de données en ajoutant simplement un intervalle de temps d'expiration aux éléments mis en cache. Une fois expiré, NCache supprime l'élément mis en cache afin que les demandes ultérieures de ces mêmes informations entraînent la mise en cache des données mises à jour. NCache offre à la fois Absolute ainsi que Glissade stratégies d'expiration et vous pouvez utiliser l'une ou l'autre en fonction de la nature transitoire des données en question.
À titre d'exemple, l'extrait de code suivant montre avec quelle facilité vous pouvez introduire une expiration absolue sur un élément de cache particulier :
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); |
Pour utiliser l'expiration glissante, tout ce que vous avez à faire est de changer le ExpirationType comme tel :
1 |
var expiration = new Expiration(ExpirationType.Sliding, TimeSpan.FromMinutes(5)); |
Une exigence majeure lors de la définition des expirations dans n'importe quel cache pour maintenir la cohérence des données du cache est que les délais d'expiration définis doivent être fonction de la vitesse à laquelle la donnée spécifique change du côté du magasin de données. Si vous définissez des délais d'expiration trop courts, les données peuvent être supprimées inutilement et entraîneront un déplacement inutile et coûteux du magasin de données ; si le délai d'expiration est trop long, des données obsolètes peuvent être utilisées.
NCache Détails Expiration des données dans NCache
Trouver les valeurs optimales pour les délais d'expiration nécessite donc une connaissance approfondie des modèles de changement d'état des données, ce qui n'est généralement pas faisable. Lorsque les exigences de cohérence du cache deviennent plus strictes, les stratégies de synchronisation de base de données sont l'approche recommandée. NCache fournit plusieurs stratégies de synchronisation de base de données à cet égard.
Grâce à ceux-ci, vous pouvez synchroniser le cache avec le magasin de données sans avoir à entrer dans les modèles d'accès aux données de chaque élément d'information, comme cela est requis lors de l'utilisation de l'expiration. Désormais, chaque fois qu'il y a un changement dans cet élément du côté du magasin de données, le cache peut alors supprimer cet élément automatiquement sans plus tarder.
Pour voir cela en action, l'extrait de code suivant montre comment vous pouvez synchroniser NCache avec une base de données SQL Server en ajoutant NCache Dépendance SQL sur les éléments mis en 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); } |
Requête SQL sur le cache
NCache offre à vos microservices la possibilité d'interroger les données de cache indexées via un mécanisme d'interrogation de type SQL. Cette fonctionnalité s'avère précieuse dans les cas où les valeurs des clés sur lesquelles les informations requises sont stockées sont inconnues. Cela supprime également une grande partie des appels d'API de cache de niveau inférieur et rend votre code d'application assez facile à comprendre et à entretenir. Cette fonctionnalité s'avère particulièrement adaptée pour vous si vous êtes plus à l'aise avec les commandes de type SQL.
Un exemple d'extrait de code illustrant l'utilisation de NCache La fonctionnalité de requête SQL est donnée ci-dessous :
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 } |
Les requêtes SQL peuvent fonctionner avec des index de requête, le NCache des structures de données distribuées ainsi que des balises de cache. Suivez le lien pour plus d'informations sur comment utiliser le NCache Fonctionnalité de requête SQL.
NCache Détails Requête SQL dans NCache
Lecture et écriture
Le NCache Fournisseurs de sources de données jeu de fonctionnalités NCache en tant qu'entrée unique dans la couche d'accès aux données du point de vue du microservice ; si un microservice a besoin de données, il n'a qu'à accéder au cache. Le cache fournit ensuite les données si elles sont disponibles dans le cache, mais si ce n'est pas le cas, il continue et récupère les données du magasin de données à l'aide d'un gestionnaire de lecture au nom du client, les met en cache et les présente au microservice.
De même, en utilisant un gestionnaire d'écriture, un microservice n'a qu'à exécuter une opération d'écriture (Ajouter, Mettre à jour, Supprimer) sur le cache et le cache effectue ensuite automatiquement l'opération d'écriture appropriée sur le magasin de données.
De plus, vous pouvez même forcer le cache à récupérer les données directement à partir du magasin de données, que le cache en contienne ou non une version périmée. Ceci est essentiel lorsque le microservice nécessite des informations à jour et s'appuie sur les stratégies de cohérence du cache mentionnées précédemment.
Non seulement la fonction de fournisseur de source de données de sauvegarde rationalise votre code d'application, mais lorsqu'elle est utilisée avec les nombreux NCache fonctionnalités de synchronisation de base de données disponibles, le cache est conservé avec des données fraîches auto-rechargées prêtes pour le calcul.
L'extrait de code suivant vous aidera à commencer à utiliser Read-Thru dans vos microservices :
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); |
De même, vous pouvez implémenter Write-Through en utilisant :
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); |
Pour plus d'informations sur l'utilisation de ces fournisseurs, reportez-vous à notre documentation à l'adresse Mise en cache de lecture ainsi que Mise en cache d'écriture immédiate.
NCache Détails Fournisseurs de sources de données dans NCache
Mise en cache du noyau EF
Entity Framework (EF) Noyau est un puissant mappeur relationnel objet (O/RM) fréquemment utilisé dans les applications .NET d'entreprise. Et parce qu'il est si populaire, NCache offre une Fournisseur de mise en cache EF Core qui vous permet d'ajouter de manière transparente la mise en cache dans le code associé à EF Core à l'aide de méthodes d'extension telles que FromCache. Cela permet aux développeurs EF Core qui ne sont pas intimement familiarisés avec NCache API pour continuer à profiter de la puissance de NCache.
Le code suivant montre la facilité d'utilisation de NCache Fournisseur de mise en cache EF Core pour introduire la mise en cache dans votre logique d'application de microservice existante.
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); |
Vous pouvez trouver plus d'informations sur l'API du fournisseur de mise en cache EF Core et comment elle peut aider votre analyse de rentabilisation sur NCache Fournisseur de base EF.
NCache Détails Mise en cache du noyau EF dans NCache
En résumé
Les microservices sont construits avec l'intention spécifique qu'ils soient autonomes ; que vous pouvez les développer, les tester et les déployer indépendamment des autres microservices. Cela permet de rendre l'ensemble de l'application hautement évolutive tout en étant ouvert aux processus rapides d'intégration continue/déploiement continu (CI/CD).
Cependant, malgré tous les avantages qu'offrent les microservices en termes d'évolutivité et de cycles de développement rapides, certains aspects d'une pile d'applications causent des problèmes. Parmi ces aspects figurent les bases de données relationnelles qui ne permettent pas l'évolution nécessaire pour faire face à une charge accrue et c'est là qu'une solution de mise en cache distribuée comme NCache brille.
NCache dispose de nombreuses fonctionnalités prêtes à l'emploi pour vous aider à faire de la mise en cache des données un ajout indolore et intuitif à votre application de microservices. Ceux-ci incluent la synchronisation de la base de données, l'expiration, la mise en cache EF Core, la requête SQL et beaucoup plus.