Asp.Net Core 轻松学-正确使用分布式缓存 (2)

在缓存过期后,每次调用 Get/GetAsync 方法都会 调用 SqlServerCache 的 私有方法 ScanForExpiredItemsIfRequired() 进行一次扫描,然后清除所有过期的缓存条目,扫描方法执行过程也很简单,就是直接执行数据库查询语句

DELETE FROM {0} WHERE @UtcNow > ExpiresAtTime

值得注意的是,在异步方法中使用同步调用不会触发缓存逐出,因为其线程退出导致 Task.Run 未能运行,比如下面的代码

[HttpGet("GetTime")] public async Task<ActionResult<string>> GetTime() { var CurrentTime = this.cache.GetString("CurrentTime"); return CurrentTime; }

将导致 SqlServerCache 无法完整执行方法 ScanForExpiredItemsIfRequired(),因为其内部使用了 Task 进行异步处理,正确的做法是使用 await this.cache.GetStringAsync("CurrentTime");

1.6 关于缓存清理方法 ScanForExpiredItemsIfRequired private void ScanForExpiredItemsIfRequired() { var utcNow = _systemClock.UtcNow; if ((utcNow - _lastExpirationScan) > _expiredItemsDeletionInterval) { _lastExpirationScan = utcNow; Task.Run(_deleteExpiredCachedItemsDelegate); } }

在多线程环境下,该方法可能除非多次重复扫描,即可能会多次执行 SQL 语句 DELETE FROM {0} WHERE @UtcNow > ExpiresAtTime ,但是,这也仅仅是警告而已,并没有任何可改变其行为的控制途径

1.7 IDistributedCache 的其它扩展方法

.Net Core 中还对 IDistributedCache 进行了扩展,甚至允许通过 Set 方法传入一个 DistributedCacheEntryOptions 以覆盖全局设置,这些扩展方法的使用都比较简单,直接传入相应的值即可,在此不再一一介绍
希望深入研究的同学,可以手动逐一测试

1.8 关于 AddDistributedSqlServerCache() 方法

AddDistributedSqlServerCache 方法内部实际上是进行了一系列的注册操作,其中最重要的是注册了 SqlServerCache 到 IDistributedCache 接口,该操作使得我们可以在控制器中采用依赖注入的方式使用 IDistributedCache 的实例
查看 AddDistributedSqlServerCache 方法的代码片段

public static IServiceCollection AddDistributedSqlServerCache(this IServiceCollection services, Action<SqlServerCacheOptions> setupAction) { if (services == null) { throw new ArgumentNullException(nameof(services)); } if (setupAction == null) { throw new ArgumentNullException(nameof(setupAction)); } services.AddOptions(); AddSqlServerCacheServices(services); services.Configure(setupAction); return services; } internal static void AddSqlServerCacheServices(IServiceCollection services) { services.Add(ServiceDescriptor.Singleton<IDistributedCache, SqlServerCache>()); } 2. 使用 Redis 分布式缓存

要在 Asp.Net Core 项目中使用 Redis 分布式缓存,需要引用包:Microsoft.Extensions.Caching.Redis,.Net Core 中的 Redis 分布式缓存客户端由 RedisCache 类提供实现 ,RedisCache 位于程序集 Microsoft.Extensions.Caching.StackExchangeRedis.dll 中,该程序集正是是依赖于大名鼎鼎的 Redis 客户端 StackExchange.Redis.dll,StackExchange.Redis 有许多的问题,其中最为严重的是超时问题,不过这不知本文的讨论范围,如果你希望使用第三方 Redis 客户端替代 StackExchange.Redis 来使用分布式缓存,你需要自己实现 IDistributedCache 接口,好消息是,IDistributedCache 接口并不复杂,定义非常简单

2.1 在 Startup.cs 中注册 Redis 分布式缓存配置 public void ConfigureServices(IServiceCollection services) { services.AddDistributedRedisCache(options => { options.InstanceName = "TestDb"; options.Configuration = this.Configuration["RedisConnectionString"]; }); ... }

注册 Redis 分布式缓存配置和使用 StackExchange.Redis 的方式完全相同,需要注意的是 RedisCacheOptions 包含 3 个属性,而 Configuration 和 ConfigurationOptions 的作用是相同的,一旦设置了 ConfigurationOptions ,就不应该再去设置属性 Configuration 的值,因为,在 AddDistributedRedisCache() 注册内部,会判断如果设置了 ConfigurationOptions 的值,则不再使用 Configuration;但是,我们建议还是通过属性 Configuration 去初始化 Redis 客户端,因为,这是一个连接字符串,而各种配置都可以通过连接字符串进行设置,这和使用 StackExchange.Redis 的方式是完全一致的

2.2 使用缓存 [Route("api/Home")] [ApiController] public class HomeController : Controller { private IDistributedCache cache; public HomeController(IDistributedCache cache) { this.cache = cache; } [HttpGet("Index")] public async Task<ActionResult<string>> SetTime() { var CurrentTime = DateTime.Now.ToString(); await this.cache.SetStringAsync("CurrentTime", CurrentTime); return CurrentTime; } [HttpGet("GetTime")] public async Task<ActionResult<string>> GetTime() { var CurrentTime = await this.cache.GetStringAsync("CurrentTime"); return CurrentTime; } }

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wpgjzs.html