在本文中,我们将介绍分布式缓存解决方案如何显着提高基于微服务的应用程序的整体性能和吞吐量。
在典型的基于微服务的应用程序中,多个微服务协同工作,同时保持松散耦合和可扩展性。 该应用程序具有满足核心业务需求所需的服务,例如跟踪和处理关键业务数据。 还有一些额外的专用微服务可以处理身份和身份验证、健康和负载监控,以及充当 API 网关。
这种应用程序的一个关键特性是每个微服务都可以使用您想要的任何技术堆栈独立设计、开发和部署。 因为每个微服务本身就是一个独立的自治应用程序,所以它也保留其独立的持久存储,无论是关系数据库, NoSQL DB 甚至遗留文件存储系统。 这允许单个微服务独立扩展,并使实时基础架构更改更易于管理。
NCache 更多信息 微服务与 NCache 在微服务中扩展 Pub/Sub
为什么你的微服务需要 NCache?
在某些情况下,在应用程序上的事务增加期间仍然会出现瓶颈。 这在微服务将数据存储在不允许扩展的关系数据库中的架构中非常常见。 在这种情况下,通过部署更多微服务实例来扩展微服务并不能解决问题。
为了解决这些问题,您可以无缝引入 NCache 作为微服务和数据存储之间缓存层的分布式缓存。 NCache 还有助于作为内存中可扩展的发布者/订阅者消息传递代理,以允许微服务之间的异步通信。
通过发布/订阅的可扩展性
微服务通信经常使用发布者/订阅者模型实现,该模型允许在微服务之间进行消息传递,同时保持它们松散耦合。 在这方面, NCache 作为内存中可扩展的 Pub/Sub 消息传递代理,组成应用程序的所有微服务都可以通过它发布和订阅事件。 与固有的可扩展性和可靠性特性 NCache 当我们来到 pub/sub 时,集群会自动翻译。 了解更多关于 NCache 作为微服务环境中的消息代理,通过我们的博客 使用 In-Memory Pub/Sub 扩展 .NET 微服务通信.
NCache 更多信息 发布/订阅消息 NCache 在微服务中扩展 Pub/Sub
通过缓存的可扩展性
NCache 提供实时可扩展性,让您可以在运行的缓存集群中添加任意数量的服务器节点,而不会导致任何应用程序停机。 使用 NCache 不仅通过充当快速内存存储提高了单个微服务的整体性能,而且还通过其集群架构促进了应用程序响应时间和可用性的显着增加。 在考虑涉及分布在多个主机上的数十个微服务的工作流时尤其如此。
使用方法 NCache 用于数据缓存?
NCache 在适当的地方,如果微服务需要数据,它首先检查缓存而不是每次都直接访问数据库。 考虑到最常访问的数据通常只占数据存储中全部可用数据的一小部分,将这些数据缓存起来并可供使用可以大大减少与数据库相关的延迟并减轻数据库的负载,因为大多数情况下的数据请求由缓存本身提供服务。
鉴于基于微服务的应用程序本质上比使用单体设计结构构建的应用程序慢,很明显您需要通过 NCache. NCache,在微服务级别,可以帮助利用微服务架构的力量,同时减少在跨多个服务顺序工作的长事务期间观察到的整体延迟。
NCache 有几个开箱即用的功能,可以对缓存操作进行细粒度控制。 这些操作包括使用过期和数据库同步来强制缓存一致性,丰富的 API 有助于使用支持源提供程序实现缓存端和缓存直通功能。 NCache 还使用 SQL 查询在缓存上提供类似 SQL 的查询操作,并充当 EF Core 等对象关系映射器 (ORM) 的缓存提供程序。
开始使用 NCache 在基于微服务的应用程序中,您需要做的第一件事就是配置服务。 这提供了您的微服务开始使用所需的信息 NCache. 概述如何在两者之间创建相互上下文 NCache 您的基于微服务的应用程序如下所示:
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()); } |
您现在需要做的是部署一个控制器,该控制器具有允许它从缓存中找到项目的逻辑。 如果没有,则控制器从数据库中获取项目并将其存储在缓存中。 这种控制器的实现如下所示:
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 在微服务中。
始终保持缓存新鲜
与底层主数据存储的内容相比,使用与保留“陈旧”数据的缓存相关的缓存有一个重要的警告。 为确保任何给定的微服务从其缓存中接收新数据,您需要定期更新缓存数据。 幸运的是, NCache 提供诸如 数据库同步 和 過期 以确保数据与主数据存储中的数据保持一致。
您可以通过简单地为缓存项添加过期时间间隔来保持缓存与数据存储的同步级别。 一旦过期, NCache 删除缓存的项目,以便对相同信息的后续请求导致更新的数据被缓存。 NCache 提供两者 绝对 以及 滑动 到期策略,您可以根据相关数据的瞬态性质使用其中任何一种。
例如,以下代码片段显示了您可以多么轻松地在特定缓存项上引入绝对过期:
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); |
要使用滑动过期,您所要做的就是像这样更改 ExpirationType:
1 |
var expiration = new Expiration(ExpirationType.Sliding, TimeSpan.FromMinutes(5)); |
在任何缓存中设置过期时间以保持缓存数据一致性的一个主要要求是,设置的过期时间必须根据特定数据在数据存储端的变化速度而定。 如果您将过期时间设置得太短,数据可能会被不必要地删除,并会导致不必要且昂贵的数据存储之旅; 如果过期时间太长,可能会使用过时的数据。
因此,找到过期时间的最佳值需要深入了解数据状态更改模式,这通常是不可行的。 当缓存一致性要求变得更加严格时,数据库同步策略是推荐的方法。 NCache 在这方面提供了几种数据库同步策略。
使用这些,您可以将缓存与数据存储同步,而不必像使用过期时那样深入研究每条信息的数据访问模式。 现在,只要数据存储端的那个项目有任何变化,缓存就可以自动删除那个项目,而不会进一步延迟。
为了看到这一点,下面的代码片段演示了如何同步 NCache 通过添加 SQL Server 数据库 NCache SQL 依赖 在缓存的项目上。
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); } |
缓存上的 SQL 查询
NCache 为您的微服务提供通过类似 SQL 的查询机制查询索引缓存数据的能力。 在存储所需信息的密钥值未知的情况下,此功能被证明是有价值的。 这也抽象出许多较低级别的缓存 API 调用,并使您的应用程序代码更容易理解和维护。 如果您更习惯于使用类似 SQL 的命令,那么这个特性被证明特别适合您。
演示使用的示例代码片段 NCache SQL查询功能如下:
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 } |
SQL 查询可以使用查询索引, NCache 分布式数据结构以及缓存标签。 点击链接了解更多信息 如何使用 NCache SQL查询功能.
直读和直写
使用 NCache 数据源提供者 功能,设置 NCache 从微服务的角度来看,作为进入数据访问层的单一入口; 如果一个微服务需要数据,它只需要访问缓存。 然后,如果缓存中的数据可用,缓存会提供数据,但如果不可用,它会继续并代表客户端使用 read-thru 处理程序从数据存储中检索数据,将其缓存并将其呈现给微服务。
类似地,通过使用直写处理程序,微服务只需对缓存执行写操作(添加、更新、删除),然后缓存自动对数据存储执行相关的写操作。
更重要的是,您甚至可以强制缓存直接从数据存储中检索数据,而不管缓存是否持有可能过时的数据版本。 当微服务需要最新信息并建立在前面提到的缓存一致性策略之上时,这一点至关重要。
支持数据源提供程序功能不仅可以简化您的应用程序代码,而且与许多 NCache 可用的数据库同步功能,缓存与自动重新加载的新数据一起保存,以备计算。
以下代码片段将帮助您开始在微服务中使用 Read-Thru:
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); |
同样,您可以使用以下方法实现直写:
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); |
有关如何使用这些提供程序的更多详细信息,请参阅我们的文档: 直读缓存 和 直写缓存.
EF 核心缓存
实体框架 (EF) 核心 是一个功能强大的对象关系映射器 (O/RM),经常用于企业 .NET 应用程序中。 而且因为它很受欢迎, NCache 提供了一个 EF Core 缓存提供程序 它允许您使用扩展方法(例如 FromCache)在 EF Core 相关代码中无缝添加缓存. 这使得不熟悉的 EF Core 开发人员 NCache API 仍然可以利用 NCache.
下面的代码演示了易用性 NCache EF Core 缓存提供程序在您现有的微服务应用程序逻辑中引入缓存。
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); |
您可以在以下位置找到有关 EF Core 缓存提供程序 API 以及它如何帮助您的业务案例的更多信息 NCache EF 核心提供程序.
总结一下
构建微服务的特定意图是它们应该是自治的; 您可以独立于其他微服务开发、测试和部署它们。 这有助于使整个应用程序具有高度可扩展性以及对快速持续集成/持续部署 (CI/CD) 流程开放。
然而,尽管微服务在可扩展性和快速开发生命周期方面提供了所有优势,但应用程序堆栈的某些方面会导致故障。 在这些方面中,关系数据库不允许扩展以应对增加的负载,这就是分布式缓存解决方案,如 NCache 闪耀。
NCache 具有许多开箱即用的功能,可帮助您使数据缓存成为微服务应用程序的轻松且直观的补充。 这些包括数据库同步、过期、EF Core 缓存、SQL 查询和 更多.