这篇文章讲解分布式缓存,即 Distributed caching in ASP.NET Core
Distributed caching in ASP.NET Core
分布式缓存是可以在多个应用服务上共享的缓存,比较经典的用法是作为多个应用服务器的一个可到达的外部服务。分布式缓存可以提高 ASP.NET Core应用的性能和扩展性(performance and scalability), 特别是当应用是部署在云服务器或者服务器集群上时。
分布式缓存比其他缓存存储在独立服务器上的场景有几个优点。
当缓存数据是分布式的:
- 跨多个服务器的请求时,数据是一致的
- 比服务器重启和应用部署存在的时间长
- 不使用本地内存
不同分布式缓存的实现,它们的配置不同。这篇文章会讲解怎样配置 SQL Server分布式缓存 和 Redis分布式缓存 。第三方实现也是可用的,例如,NCache. 不管选择那一种实现,应用和缓存都是使用 IDistributedCache接口交互。
一.Prerequisites(先决条件)
1. 要使用SQL Server分布式缓存,需要引入 Microsoft.AspNetCore.App metapackage 或者 添加 Microsoft.Extensions.Caching.SqlServer 包的引用
2. 要使用Redis 分布式缓存,需要引用 Microsoft.AspNetCore.App metapackage 并且添加 Microsoft.Extensions.Caching.StackExchangeRedis 包的引用。因为Redis包没有包含在 Microsoft.AspNetCore.App 中,所以必须分开引用Redis包。
二. IDistributedCache interface
IDistributedCache接口提供下列方法来操作分布式缓存中的数据项:
- Get, GetAsync :接受string类型的key, 如果在缓存中找到了,缓存数据会以byte[]数组的形式输出
- Set, SetAsync :往缓存中添加缓存数据
- Refresh, RefreshAsync : 根据key值,刷新缓存中的数据项,重置它的过期时间
- Remove, RemoveAsync : 删除缓存的数据项
三. Establish distributed caching services (建立分布式服务)
在Startup.ConfigureServices中注册一个IDistributedCache的实现。Framework提供的实现在这个主题会做一些描述,包括:
- Distributed Memory Cache(分布式内存缓存)
- Distributed SQL Server cache(分布式SQL Server缓存)
- Distributed Redis cache(分布式Redis缓存)
1. Distributed Memory Cache
Distributed Memory Cache 是存储在内存中的 , 它不是一个现实(actual)中的分布式缓存。缓存数据是存储在应用运行的服务器上的。
分布式内存缓存是一个有用的实现:
在开发和测试场景
当生产环境是在一个单独的服务器,并且内存消耗不是一个问题时。实现分布式内存缓存来简化数据存储。它允许在将来实现一个真正的分布式缓存解决方案如果多个结点或者容错成为可能 。(Implementing the Distributed Memory Cache abstracts cached data storage. It allows for implementing a true distributed caching solution in the future if multiple nodes or fault tolerance become necessary.)
这个示例中,应用是运行在开发环境,在 Startup.ConfigureServices中 ,使用 Distributed Memory Cache :
services.AddDistributedMemoryCache();
2. Distributed SQL Server Cache
分布式SQL Server 缓存实现允许分布式缓存使用SQL Server数据库作为一个存储备份。要在SQL Server实例中创建一个SQL Server缓存项(cached item),你应该用sql-cache工具。这个工具用你提供的name和schema创建一个table.
在SQL Server中通过运行sql-cache create命令创建一个table. 提供SQL Server实例(Data Source),数据库(Initial Catalog),schema(例如, dbo),和表名(例如,TestCache):
dotnet sql-cache create "Data Source=(localdb)\MSSQLLocalDB;Initial Catalog=DistCache;Integrated Security=True;" dbo TestCache
成功后,显示:
Table and index were created successfully.
通过sql-cache工具创建的表(table)有下列的schema:
注意:应用在操作缓存值时,应该使用IDistributedCache,而不是一个SqlServerCache.即使用接口的方式
这个示例应用实现了SqlServerCache,在非开发环境,在Startup.ConfigureServices:
services.AddDistributedSqlServerCache(options => { options.ConnectionString = _config["DistCache_ConnectionString"]; options.SchemaName = "dbo"; options.TableName = "TestCache"; });
3. Distributed Redis Cache
Redis是一个开源的in-memory 数据存储,它经常用作一个分布式缓存。你可以在本地使用Redis,并且你可以配置Azure Redis Cache为一个Azure-hosted ASP.NET Core应用。
一个应用配置缓存实现,使用一个RedisCache实例在一个非开发环境,在Startup.ConfigureServices:
services.AddStackExchangeRedisCache(options => { options.Configuration = "localhost"; options.InstanceName = "SampleInstance"; });
使用本地机器上的Redis时,需要下载Redis,并且运行redis-server 。
好了,上面的1,2,3是讲解不同缓存在 Startup.ConfigureServices 中的配置,使添加到项目中。下面讲下如何使用
四.Use the distributed cache
要使用IDistributedCache接口,可以从应用中的任意构造函数中,请求一个IDistributedCache实例.这个实例通过依赖注入提供。
当应用启动时,IDistributedCache被注入到Startup.Configure中。使用IApplicationLifetime使当前时间被缓存。
public void Configure(IApplicationBuilder app, IHostingEnvironment env, IApplicationLifetime lifetime, IDistributedCache cache) { lifetime.ApplicationStarted.Register(() => { var currentTimeUTC = DateTime.UtcNow.ToString(); //当前时间 byte[] encodedCurrentTimeUTC = Encoding.UTF8.GetBytes(currentTimeUTC); var options = new DistributedCacheEntryOptions() .SetSlidingExpiration(TimeSpan.FromSeconds(20)); cache.Set("cachedTimeUTC", encodedCurrentTimeUTC, options); });
在这个示例应用中,会把 IDistributedCache注入到IndexModel中被Index page使用。
代码如下:
public class IndexModel : PageModel { private readonly IDistributedCache _cache; public IndexModel(IDistributedCache cache) { _cache = cache; } public string CachedTimeUTC { get; set; } public async Task OnGetAsync() { CachedTimeUTC = "Cached Time Expired"; var encodedCachedTimeUTC = await _cache.GetAsync("cachedTimeUTC"); if (encodedCachedTimeUTC != null) { CachedTimeUTC = Encoding.UTF8.GetString(encodedCachedTimeUTC); } } public async Task<IActionResult> OnPostResetCachedTime() { var currentTimeUTC = DateTime.UtcNow.ToString(); byte[] encodedCurrentTimeUTC = Encoding.UTF8.GetBytes(currentTimeUTC); var options = new DistributedCacheEntryOptions() .SetSlidingExpiration(TimeSpan.FromSeconds(20)); await _cache.SetAsync("cachedTimeUTC", encodedCurrentTimeUTC, options); return RedirectToPage(); } }
注意:对于IDistributedCache实例,没有必要使用单例(Singleton)或者Scoped lifetime。(至少对于内置的实现没有必要)
五.Recommendations
当考虑使用IDistributedCache的哪一种实现对于你的应用最合适时,可以考虑下:
Existing infrastructure 已存在的基础设施
Performance requirements 表现(性能)要求
Cost 花销
Team experience 团队经验
缓存解决方案通常依赖in-memory storage(内存存储)来提供对缓存数据的快速检索。但是内存是一个有限的资源,并且很难扩展(costly to expand;costly,昂贵的)。仅将常用数据存储在缓存中。
通常来说,一个Redis cache比一个SQL Server cache 能提供更高的吞吐量(throughput:生产量,生产能力,吞吐量),并且更低的潜伏因素(latency:潜伏,潜伏因素). 然而,大家通常使用 benchmarking来判断the performance characteristics of caching strategies(缓存策略的表现性能)。
当SQL Server被用作一个分布式缓存备份存储。使用同一个数据库来缓存和普通数据的存储,和检索,会消极的影响两者的表现。我们建议为分布式缓存备份存储使用一个专用的SQL Server实例。
参考资料:
https://docs.microsoft.com/en-us/aspnet/core/performance/caching/distributed?view=aspnetcore-2.2