.NET WebAPI 采用 IDistributedCache 实现分布式缓存过滤器 Redis 模式

分布式缓存是由多个应用服务器共享的缓存,通常作为访问它的应用服务器的外部服务进行维护。 分布式缓存可以提高 ASP.NET Core 应用的性能和可伸缩性,尤其是当应用由云服务或服务器场托管时。

与其他将缓存数据存储在单个应用服务器上的缓存方案相比,分布式缓存具有多个优势。

当分发缓存数据时,数据:

  • 在多个服务器的请求之间保持一致(一致性)。
  • 在进行服务器重启和应用部署后仍然有效。
  • 不使用本地内存。

实现方案采用 Redis 作为缓存的数据托管方案,接口使用微软官方的 IDistributedCache 接口实现。

首选安装 Microsoft.Extensions.Caching.StackExchangeRedis 组件包

然后注入 分布式缓存服务

//注册缓存服务 Redis模式 builder.Services.AddStackExchangeRedisCache(options => {     options.Configuration = builder.Configuration.GetConnectionString("redisConnection");     options.InstanceName = "cache"; });

 

IDistributedCache 的扩展类,后面过滤器操作缓存需要用到这个扩展方法。

using Microsoft.Extensions.Caching.Distributed;  namespace Common {      /// <summary>     /// 扩展分布式缓存接口,集成常用方法     /// </summary>     public static class IDistributedCacheExtension     {          /// <summary>         /// 删除指定key         /// </summary>         /// <param name="key"></param>         /// <returns></returns>         public static bool Remove(this IDistributedCache distributedCache, string key)         {             try             {                 distributedCache.Remove(key);                 return true;             }             catch             {                 return false;             }         }           /// <summary>         /// 设置object类型的key         /// </summary>         /// <param name="key"></param>         /// <param name="value"></param>         /// <returns></returns>         public static bool SetObject(this IDistributedCache distributedCache, string key, object value)         {             try             {                 var valueStr = JsonHelper.ObjectToJson(value);                 distributedCache.SetString(key, valueStr);                 return true;             }             catch             {                 return false;             }         }           /// <summary>         /// 设置string类型key,包含有效时间         /// </summary>         /// <param name="key"></param>         /// <param name="value"></param>         /// <param name="timeOut"></param>         /// <returns></returns>         public static bool SetString(this IDistributedCache distributedCache, string key, string value, TimeSpan timeOut)         {             try             {                 distributedCache.SetString(key, value, new DistributedCacheEntryOptions { AbsoluteExpirationRelativeToNow = timeOut });                 return true;             }             catch             {                 return false;             }         }           /// <summary>         /// 设置object类型key,包含有效时间         /// </summary>         /// <param name="key"></param>         /// <param name="value"></param>         /// <param name="timeOut"></param>         /// <returns></returns>         public static bool SetObject(this IDistributedCache distributedCache, string key, object value, TimeSpan timeOut)         {             try             {                 var valueStr = JsonHelper.ObjectToJson(value);                 distributedCache.SetString(key, valueStr, new DistributedCacheEntryOptions { AbsoluteExpirationRelativeToNow = timeOut });                 return true;             }             catch             {                 return false;             }         } 
/// <summary> /// 读取 Object 类型的key /// </summary> /// <typeparam name="T"></typeparam> /// <param name="key"></param> /// <returns></returns> public static T GetObject<T>(this IDistributedCache distributedCache, string key) { try { var valueStr = distributedCache.GetString(key); var value = JsonHelper.JsonToObject<T>(valueStr); return value; } catch { return default!; } }
/// <summary> /// 判断是否存在指定key /// </summary> /// <param name="key"></param> /// <returns></returns> public static bool IsContainKey(this IDistributedCache distributedCache, string key) { if (string.IsNullOrEmpty(distributedCache.GetString(key))) { return false; } else { return true; } } } }

 

WebAPI 的缓存过滤器代码如下:

using Common; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Filters; using Microsoft.Extensions.Caching.Distributed; using System.Text.Json;  namespace WebAPI.Filters {      /// <summary>     /// 缓存过滤器     /// </summary>     [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]     public class CacheDataFilter : Attribute, IActionFilter     {          /// <summary>         /// 缓存时效有效期,单位 秒         /// </summary>         public int TTL { get; set; }           /// <summary>         /// 是否使用 Token         /// </summary>         public bool IsUseToken { get; set; }           void IActionFilter.OnActionExecuting(ActionExecutingContext context)         {             string key = "";              if (IsUseToken)             {                 var token = context.HttpContext.Request.Headers.Where(t => t.Key == "Authorization").Select(t => t.Value).FirstOrDefault();                  key = context.ActionDescriptor.DisplayName + "_" + context.HttpContext.Request.QueryString + "_" + token;             }             else             {                 key = context.ActionDescriptor.DisplayName + "_" + context.HttpContext.Request.QueryString;             }              key = "CacheData_" + CryptoHelper.GetMD5(key);              try             {                 var distributedCache = context.HttpContext.RequestServices.GetRequiredService<IDistributedCache>();                 var cacheInfo = distributedCache.GetObject<object>(key);                  if (cacheInfo != null)                 {                     if (((JsonElement)cacheInfo).ValueKind == JsonValueKind.String)                     {                         context.Result = new ObjectResult(cacheInfo.ToString());                     }                     else                     {                         context.Result = new ObjectResult(cacheInfo);                     }                 }             }             catch (Exception ex)             {                 var logger = context.HttpContext.RequestServices.GetRequiredService<ILogger<CacheDataFilter>>();                 logger.LogError(ex, "缓存模块异常-In");             }         }           void IActionFilter.OnActionExecuted(ActionExecutedContext context)         {             try             {                 if (context.Result is ObjectResult objectResult && objectResult.Value != null)                 {                     string key = "";                      if (IsUseToken)                     {                         var token = context.HttpContext.Request.Headers.Where(t => t.Key == "Authorization").Select(t => t.Value).FirstOrDefault();                          key = context.ActionDescriptor.DisplayName + "_" + context.HttpContext.Request.QueryString + "_" + token;                     }                     else                     {                         key = context.ActionDescriptor.DisplayName + "_" + context.HttpContext.Request.QueryString;                     }                      key = "CacheData_" + CryptoHelper.GetMD5(key);                      if (objectResult.Value != null)                     {                         var distributedCache = context.HttpContext.RequestServices.GetRequiredService<IDistributedCache>();                         distributedCache.SetObject(key, objectResult.Value, TimeSpan.FromSeconds(TTL));                     }                  }             }             catch (Exception ex)             {                 var logger = context.HttpContext.RequestServices.GetRequiredService<ILogger<CacheDataFilter>>();                 logger.LogError(ex, "缓存模块异常-Out");             }          }     } }

缓存过滤器的入参只有两个

  • TTL 缓存有效期以秒为单位
  • IsUseToken 是否使用 Token 区分不同的用户身份,之所以加入这个参数,主要是因为有些接口虽然多个用户请求时的入参一样,但是不同的用户需要返回不同的信息,所以面对这种类型的接口需要将 IsUseToken 设定为 True。

过滤器的使用方法就很简单了,直接附在对应的接口 Action 方法上就可以,如下:

[CacheDataFilter(TTL = 60, IsUseToken = true)] public DtoUser? GetUserInfo() {     ///省略业务逻辑 }

此处对于 GetUserInfo  接口添加了缓存过滤器,对数据缓存60秒钟,并且针对 不同的Token身份进行了区分,因为这边的逻辑是通过 Token 识别用户身份的,虽然请求没有特别的参数,但是需要为不同用户的请求返回对应的用户信息,并且分别缓存。

至此 .NET WebAPI 采用 IDistributedCache 实现分布式缓存过滤器 Redis 模式 就讲解完了,有任何不明白的,可以在文章下面评论或者私信我,欢迎大家积极的讨论交流,有兴趣的朋友可以关注我目前在维护的一个 .NET 基础框架项目,项目地址如下

发表评论

相关文章