-
.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; } } } }
上面的扩展方法里面用到了我自己的 JsonHelper 所以这里也贴一下 JsonHelper 的代码
using Common.JsonConverter; using System.Text.Encodings.Web; using System.Text.Json; using System.Text.Json.Nodes; namespace Common { public class JsonHelper { /// <summary> /// 通过 Key 获取 Value /// </summary> /// <returns></returns> public static string? GetValueByKey(string json, string key) { using JsonDocument doc = JsonDocument.Parse(json); var jsonElement = doc.RootElement.Clone(); return jsonElement.GetProperty(key).GetString(); } /// <summary> /// 对象 转 Json /// </summary> /// <param name="obj">对象</param> /// <returns>JSON格式的字符串</returns> public static string ObjectToJson(object obj) { JsonSerializerOptions options = new(); //关闭默认转义 options.Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping; //启用驼峰格式 options.PropertyNamingPolicy = JsonNamingPolicy.CamelCase; //关闭缩进设置 options.WriteIndented = false; return JsonSerializer.Serialize(obj, options); } /// <summary> /// Json 转 对象 /// </summary> /// <typeparam name="T">类型</typeparam> /// <param name="jsonText">JSON文本</param> /// <returns>指定类型的对象</returns> public static T JsonToObject<T>(string json) { JsonSerializerOptions options = new();//启用大小写不敏感 options.PropertyNameCaseInsensitive = true; return JsonSerializer.Deserialize<T>(json, options)!; } /// <summary> /// 没有 Key 的 Json 转 List<JToken> /// </summary> /// <param name="strJson"></param> /// <returns></returns> public static JsonNode? JsonToArrayList(string json) { var jsonNode = JsonNode.Parse(json); return jsonNode; } } }
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 基础框架项目,项目地址如下
https://github.com/berkerdong/NetEngine.git
栏目列表
最新更新
nodejs爬虫
Python正则表达式完全指南
爬取豆瓣Top250图书数据
shp 地图文件批量添加字段
爬虫小试牛刀(爬取学校通知公告)
【python基础】函数-初识函数
【python基础】函数-返回值
HTTP请求:requests模块基础使用必知必会
Python初学者友好丨详解参数传递类型
如何有效管理爬虫流量?
2个场景实例讲解GaussDB(DWS)基表统计信息估
常用的 SQL Server 关键字及其含义
动手分析SQL Server中的事务中使用的锁
openGauss内核分析:SQL by pass & 经典执行
一招教你如何高效批量导入与更新数据
天天写SQL,这些神奇的特性你知道吗?
openGauss内核分析:执行计划生成
[IM002]Navicat ODBC驱动器管理器 未发现数据
初入Sql Server 之 存储过程的简单使用
SQL Server -- 解决存储过程传入参数作为s
关于JS定时器的整理
JS中使用Promise.all控制所有的异步请求都完
js中字符串的方法
import-local执行流程与node模块路径解析流程
检测数据类型的四种方法
js中数组的方法,32种方法
前端操作方法
数据类型
window.localStorage.setItem 和 localStorage.setIte
如何完美解决前端数字计算精度丢失与数