En este artículo, veremos cómo una solución de almacenamiento en caché distribuido puede mejorar drásticamente el rendimiento general y el rendimiento de su aplicación basada en microservicios.
En una aplicación típica basada en microservicios, múltiples microservicios funcionan juntos mientras permanecen acoplados libremente y son escalables. La aplicación tiene servicios que se necesitan para satisfacer los requisitos comerciales básicos, como realizar un seguimiento y procesar datos comerciales críticos. También hay microservicios dedicados adicionales que manejan la identidad y la autenticación, el estado y la supervisión de la carga, además de servir como puertas de enlace API.
Una característica clave de una aplicación de este tipo es que cada microservicio se diseñe, desarrolle e implemente de forma independiente utilizando cualquier pila de tecnología que desee. Debido a que cada microservicio es una aplicación autónoma independiente por derecho propio, también mantiene su almacenamiento persistente separado, ya sea una base de datos relacional, un NoSQL DB o incluso un sistema de almacenamiento de archivos heredado. Esto permite que los microservicios individuales se escalen de forma independiente y hace que los cambios de infraestructura en tiempo real sean mucho más manejables.
NCache Detalles Microservicios con NCache Escale Pub/Sub en microservicios
¿Por qué su microservicio necesita NCache?
Hay casos en los que aún surgen cuellos de botella durante el aumento de transacciones en la aplicación. Esto es predominantemente común en arquitecturas donde los microservicios almacenan datos en bases de datos relacionales que no permiten el escalamiento horizontal. En tales situaciones, escalar el microservicio mediante la implementación de más instancias no resuelve el problema.
Para contrarrestar estos problemas, puede introducir sin problemas NCache como su caché distribuida en la capa de almacenamiento en caché entre sus microservicios y los almacenes de datos. NCache también ayuda como agente de mensajería de editor/suscriptor escalable en memoria para permitir comunicaciones asincrónicas entre microservicios.
Escalabilidad a través de Pub/Sub
La comunicación de microservicios se implementa con frecuencia utilizando el modelo de publicador/suscriptor que permite la mensajería entre microservicios mientras los mantiene débilmente acoplados. En ese sentido, NCache sirve como un agente de mensajería Pub/Sub escalable en memoria a través del cual todos los microservicios que componen la aplicación pueden publicar y suscribirse a eventos. La característica de escalabilidad y confiabilidad inherente a NCache el agrupamiento se traduce automáticamente cuando llegamos a pub/sub. Encuentra mas sobre NCache como intermediario de mensajes en el entorno de microservicios a través de nuestro blog en Escalamiento de la comunicación de microservicios .NET con In-Memory Pub/Sub.
NCache Detalles Mensajería Pub/Sub en NCache Escale Pub/Sub en microservicios
Escalabilidad a través del almacenamiento en caché
NCache ofrece escalabilidad en tiempo real, lo que le permite agregar tantos nodos de servidor como desee en su clúster de caché en ejecución sin incurrir en ningún tiempo de inactividad de la aplicación. Usando NCache no solo mejora el rendimiento general de los microservicios individuales al actuar como un almacén en memoria rápido, sino que también facilita un marcado aumento en el tiempo de respuesta de la aplicación y la disponibilidad a través de su arquitectura de agrupación. Esto es especialmente cierto cuando se consideran flujos de trabajo que involucran docenas de microservicios distribuidos en múltiples hosts.
Instrucciones de uso NCache para el almacenamiento en caché de datos?
Con NCache en su lugar, si un microservicio requiere datos, primero verifica el caché en lugar de acceder directamente a la base de datos cada vez. Dado que los datos a los que se accede con más frecuencia generalmente constituyen una pequeña porción de todos los datos disponibles en el almacén de datos, tener esos datos ya almacenados en caché y disponibles para su uso reduce en gran medida la latencia relacionada con la base de datos y alivia la carga en la base de datos, ya que la mayoría de las solicitudes de datos son atendidas por el propio caché.
Al ver cómo una aplicación basada en microservicios es inherentemente más lenta que cuando se crea con una estructura de diseño monolítica, está claro que necesita la ganancia en velocidad que ofrece NCache. NCache, a nivel de microservicio, puede ayudar a aprovechar el poder de una arquitectura de microservicios mientras reduce drásticamente la latencia general observada durante transacciones largas que abarcan múltiples servicios que funcionan secuencialmente.
NCache tiene varias funciones listas para usar que brindan un control detallado sobre las operaciones de almacenamiento en caché. Estas operaciones incluyen la aplicación de la coherencia de la memoria caché mediante la caducidad y la sincronización de la base de datos, API enriquecidas que ayudan a implementar funciones de caché aparte y caché mediante proveedores de fuentes de respaldo. NCache también proporciona operaciones de consulta similares a SQL en la memoria caché mediante consultas SQL y sirve como proveedores de almacenamiento en caché para Mapeadores relacionales de objetos (ORM) como EF Core.
Para comenzar con NCache en su aplicación basada en microservicios, lo primero que necesita es configurar los servicios. Esto proporciona la información requerida que sus microservicios necesitan para comenzar a usar NCache. Una descripción general de cómo puede crear un contexto mutuo entre NCache y su aplicación basada en microservicios se muestra a continuación:
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()); } |
Lo que debe hacer ahora es implementar un controlador con la lógica que le permita obtener un elemento del caché si lo encuentra allí. Y si no, entonces el controlador obtiene el elemento de la base de datos y lo almacena en el caché. La implementación de dicho controlador se muestra a continuación:
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 Detalles Microservicios con NCache
Repasemos sus detalles para conocer los verdaderos encantos de NCache en microservicios.
Mantenga el caché actualizado, siempre
Hay una advertencia importante sobre el uso de un caché que está relacionado con el caché que retiene datos "obsoletos" en comparación con el contenido del almacén de datos principal subyacente. Para asegurarse de que cualquier microservicio dado reciba datos nuevos de su caché, debe actualizar los datos de la caché con regularidad. Afortunadamente, NCache proporciona características tales como Sincronización de base de datos y Vencimiento para garantizar que los datos sean coherentes con los del almacén de datos principal.
Puede mantener un nivel de sincronización del caché con el almacén de datos simplemente agregando un intervalo de tiempo de caducidad a los elementos almacenados en caché. Una vez expirado, NCache elimina el elemento almacenado en caché para que las solicitudes posteriores de esa misma información den como resultado que los datos actualizados se almacenen en caché. NCache ofrece tanto Absoluto al igual que Corredizo estrategias de caducidad y puede utilizar cualquiera dependiendo de la naturaleza transitoria de los datos en cuestión.
Como ejemplo, el siguiente fragmento de código muestra la facilidad con la que puede introducir la caducidad absoluta en un elemento de caché en particular:
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); |
Para usar el vencimiento deslizante, todo lo que tiene que hacer es cambiar el Tipo de vencimiento como tal:
1 |
var expiration = new Expiration(ExpirationType.Sliding, TimeSpan.FromMinutes(5)); |
Un requisito importante al establecer vencimientos en cualquier caché para mantener la consistencia de los datos de caché es que los tiempos de vencimiento establecidos deben estar de acuerdo con la rapidez con la que cambia la parte específica de los datos en el lado del almacén de datos. Si configura los tiempos de caducidad demasiado cortos, es posible que los datos se eliminen innecesariamente y resulte en un viaje innecesario y costoso del almacén de datos; si el tiempo de caducidad es demasiado largo, se pueden utilizar datos obsoletos.
NCache Detalles Caducidad de datos en NCache
Por lo tanto, encontrar los valores óptimos para los tiempos de caducidad requiere un conocimiento profundo de los patrones de cambio de estado de los datos, lo que generalmente no es factible. Cuando los requisitos de consistencia de la memoria caché se vuelven más estrictos, las estrategias de sincronización de bases de datos son el enfoque recomendado. NCache proporciona varias estrategias de sincronización de bases de datos a este respecto.
Con estos, puede sincronizar la memoria caché con el almacén de datos sin tener que acceder a los patrones de acceso a los datos de cada información, como se requiere cuando se usa la caducidad. Ahora, siempre que haya algún cambio en ese elemento en el lado del almacén de datos, el caché puede eliminar ese elemento automáticamente sin más demora.
Para ver esto en acción, el siguiente fragmento de código demuestra cómo puede sincronizar NCache con una base de datos de SQL Server agregando NCache Dependencia SQL en los elementos almacenados en caché.
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); } |
Consulta SQL en caché
NCache ofrece a sus microservicios la capacidad de consultar datos de caché indexados a través de un mecanismo de consulta similar a SQL. Esta característica resulta valiosa en los casos en que se desconocen los valores de las claves con las que se almacena la información requerida. Esto también abstrae gran parte de las llamadas API de caché de nivel inferior y hace que el código de su aplicación sea bastante más fácil de entender y mantener. Esta función demuestra ser especialmente adecuada para usted si se siente más cómodo con los comandos similares a SQL.
Un fragmento de código de ejemplo que demuestra el uso de la NCache La función de consulta SQL se proporciona a continuación:
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 } |
Las consultas SQL pueden funcionar con índices de consulta, el NCache estructuras de datos distribuidas, así como etiquetas de caché. Siga el enlace para obtener más información sobre cómo utilizar el NCache Función de consulta SQL.
NCache Detalles Consulta SQL en NCache
Read-Thru y Write-Thru
Usando el NCache Proveedores de fuentes de datos conjunto de características NCache como entrada única en la capa de acceso a datos desde la perspectiva del microservicio; si un microservicio requiere datos, solo tiene que acceder al caché. Luego, el caché proporciona los datos si están disponibles en el caché, pero si no lo están, continúa y recupera los datos del almacén de datos mediante un controlador de lectura en nombre del cliente, los almacena en caché y los presenta al microservicio.
De manera similar, al utilizar un controlador de escritura directa, un microservicio solo tiene que ejecutar una operación de escritura (Agregar, Actualizar, Eliminar) en el caché y el caché luego realiza la operación de escritura relevante en el almacén de datos automáticamente.
Es más, incluso puede obligar a la memoria caché a recuperar datos directamente del almacén de datos, independientemente de si la memoria caché contiene una versión posiblemente obsoleta. Esto es fundamental cuando el microservicio requiere información actualizada y se basa en las estrategias de coherencia de caché mencionadas anteriormente.
La función de proveedor de fuente de datos de respaldo no solo agiliza el código de su aplicación, sino que cuando se usa junto con los muchos NCache funciones de sincronización de base de datos disponibles, el caché se mantiene con datos frescos recargados automáticamente listos para el cálculo.
El siguiente fragmento de código lo ayudará a comenzar a usar Read-Thru en sus microservicios:
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 manera similar, puede implementar 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); |
Para obtener información más detallada sobre cómo utilizar estos proveedores, consulte nuestra documentación en Almacenamiento en caché de lectura y Almacenamiento en caché de escritura simultánea.
NCache Detalles Proveedores de fuentes de datos en NCache
Almacenamiento en caché de EF Core
Núcleo de Entity Framework (EF) es un potente mapeador relacional de objetos (O/RM) que se utiliza con frecuencia en aplicaciones empresariales .NET. Y como es tan popular, NCache ofrece un Proveedor de almacenamiento en caché de EF Core que le permite agregar sin problemas el almacenamiento en caché dentro del código relacionado con EF Core usando métodos de extensión como FromCache. Esto permite a los desarrolladores de EF Core que no están íntimamente familiarizados con NCache API para seguir aprovechando el poder de NCache.
El siguiente código demuestra la facilidad de uso del NCache Proveedor de almacenamiento en caché de EF Core para introducir el almacenamiento en caché en su lógica de aplicación de microservicio existente.
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); |
Puede encontrar más información sobre la API del proveedor de almacenamiento en caché de EF Core y cómo puede ayudar a su caso de negocios en NCache Proveedor principal de EF.
NCache Detalles Almacenamiento en caché de EF Core en NCache
Resumiendo todo
Los microservicios se crean con la intención específica de que sean autónomos; que puede desarrollarlos, probarlos e implementarlos independientemente de los otros microservicios. Esto sirve para hacer que toda la aplicación sea altamente escalable, además de estar abierta a procesos rápidos de integración continua/implementación continua (CI/CD).
Sin embargo, a pesar de todas las ventajas que ofrecen los microservicios en términos de escalabilidad y ciclos de vida de desarrollo rápidos, hay ciertos aspectos de una pila de aplicaciones que causan problemas. Entre estos aspectos se encuentran las bases de datos relacionales que no permiten el escalamiento horizontal necesario para lidiar con una mayor carga y aquí es donde una solución de almacenamiento en caché distribuida como NCache brilla
NCache tiene numerosas funciones listas para usar para ayudarlo a hacer que el almacenamiento en caché de datos sea una adición sencilla e intuitiva a su aplicación de microservicios. Estos incluyen la sincronización de la base de datos, la caducidad, el almacenamiento en caché de EF Core, la consulta SQL y mucho más.