D_05 API缓存过滤器(aop方式实现缓存)
作者:互联网
功能描述
为了提升服务接口的性能,所以需要添加缓存。为了尽量避免添加缓存对业务代码的侵入性,所以采用aop模式(过滤器)来实现缓存功能。api缓存过滤器具备一下的能力。
(1)在过滤器中实现缓存功能,如果key存在则直接返回结果,如果不存在则进入业务层,在业务层处理完成后回到过滤器完成对缓存的更新插入;
(2)能够灵活的生成缓存key,进而实现缓存的不同场景,如可以根据路由-接口方法-参数,能动态识别get、post、put、delete方式性能,进而生成灵活的key值;
(3)api缓存过滤器是非全局的,根据自己业务按需通过特性的方式加载方法上;
(4)特性支持设置key(支持通配符)、缓存时间、是否启用缓存波动时间(可解决缓存雪崩)
(5)提供缓存过滤器ApiCache:,示例: [ApiCache("UserRoles:{id}", ApplicationConst.Cache.ApiCacheTime,true)],同时提供移除缓存过滤器,示例: [ApiCacheRemove("UserRoles:{id}")],在更新的接口中可以通过该特性动态的删除缓存,保证数据一直性。
(6)api缓存过滤器基本可以上可以覆盖常规业务上的缓存用法,但实现缓存时该方式并不是强制性的,可以根据具体情况进行选择。优先选用缓存过滤器、如果针对默写复杂的业务场景,可以降级混用,甚至再降级采用在业务代码层实现缓存。
示例截图:
设计
缓存的流程图
代码实现
api缓存过滤器实现代码
/// <summary> /// 接口缓存过滤器,备注: /// 1.适用于输入参数简单的接口缓存,例如根据Id获取实体,不适用于分页查询接口缓存; /// 2.禁用并更新缓存:Http请求头或者Url参数增加'DisableCache'; /// 3.注意缓存键模式参数'KeyPattern'要唯一,不要与其他缓存冲突; /// 4.只对返回值类型为通用接口类型'ApiResult'的接口进行缓存; /// 5.在更新和删除接口方法上使用'ApiCacheRemove'特性进行缓存的删除; /// 6.模板类型参数名称注意大小写,Json对大小写敏感,Http首部Hearder,路由参数及Url参数对大小写不敏感,所以从Json中解析参数值时需保证参数名与模板参数名称大小写一致; /// </summary> public class ApiCacheAttribute : ActionFilterAttribute { /// <summary> /// 缓存键模式 /// </summary> public string KeyPattern { get; set; } /// <summary> /// 缓存分钟 /// </summary> public int CacheMinutes { get; set; } /// <summary> /// 是否添加随机值(预防缓存雪崩),默认:true /// </summary> public bool EnableRandomTime { get; set; } private RedisClient _redisClient; private ICurrentTenant _currentTenant; private string _cacheKey; /// <summary> /// 构造函数 /// </summary> /// <param name="keyPattern">缓存键模式</param> /// <param name="cacheMinutes">缓存分钟</param> /// <param name="enableRandomTime">是否添加随机值(预防缓存雪崩)</param> public ApiCacheAttribute(string keyPattern, int cacheMinutes = 5, bool enableRandomTime = true) { KeyPattern = keyPattern; CacheMinutes = cacheMinutes; EnableRandomTime = enableRandomTime; } public override void OnActionExecuting(ActionExecutingContext context) { _redisClient = context.HttpContext.RequestServices.GetRequiredService<RedisClient>(); _currentTenant = context.HttpContext.RequestServices.GetRequiredService<ICurrentTenant>(); _cacheKey = KeyPattern.ToApiCacheKey(context.HttpContext.Request, _currentTenant.Id); if (context.HttpContext.Request.Query != null && context.HttpContext.Request.Query.ContainsKey("DisableCache") || context.HttpContext.Request.Headers.ContainsKey("DisableCache")) { return; } string apiResultStr = _redisClient.StringGet(_cacheKey); if (!string.IsNullOrWhiteSpace(apiResultStr)) { var content = new ContentResult(); content.Content = apiResultStr; content.ContentType = "application/json; charset=utf-8"; content.StatusCode = 200; context.HttpContext.Response.Headers.Add("CacheData", _cacheKey); context.Result = content; } } public override void OnActionExecuted(ActionExecutedContext context) { if (context.Exception != null) return; if (context.Result == null) return; var objResult = context.Result as ObjectResult; if (objResult.DeclaredType != typeof(ApiResult)) return; var apiResult = objResult.Value as ApiResult; if (apiResult.Code != (int)ApiEnum.Success) return; var expireTime = TimeSpan.FromMinutes(CacheMinutes); if (apiResult.Data == null) { //如果数据不存在,则把空值缓存起来并把过期时间设置为一个比较短的时间,目的是对缓存击穿进行一定控制 expireTime = TimeSpan.FromMinutes(1); } if (EnableRandomTime) { expireTime += TimeSpan.FromSeconds(new Random().Next(300)); } var jsonSettings = JsonSerializerSettingsProvider.CreateSerializerSettings(); jsonSettings.DateFormatString = "yyyy-MM-dd HH:mm:ss"; jsonSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore; var apiResultStr = JsonConvert.SerializeObject(apiResult, jsonSettings); _redisClient.StringSet(_cacheKey, apiResultStr, expireTime); } }
api缓存移除过滤器实现代码
/// <summary> /// 接口缓存删除过滤器 /// </summary> public class ApiCacheRemoveAttribute : ActionFilterAttribute { /// <summary> /// 缓存键模式,备注: /// 1.模板类型参数名称注意大小写,Json对大小写敏感,Http首部Hearder,路由参数及Url参数对大小写不敏感,所以从Json中解析参数值时需保证参数名与模板参数名称大小写一致; /// </summary> public string KeyPattern { get; set; } private RedisClient _redisClient; private ICurrentTenant _currentTenant; private string _cacheKey; /// <summary> /// 构造函数 /// </summary> /// <param name="keyPattern">缓存键模式</param> public ApiCacheRemoveAttribute(string keyPattern) { KeyPattern = keyPattern; } public override void OnActionExecuted(ActionExecutedContext context) { if (context.Exception != null) return; if (context.Result == null) return; var objResult = context.Result as ObjectResult; if (objResult.DeclaredType != typeof(ApiResult)) return; var apiResult = objResult.Value as ApiResult; if (apiResult.Code != (int)ApiEnum.Success) return; _redisClient = context.HttpContext.RequestServices.GetRequiredService<RedisClient>(); _currentTenant = context.HttpContext.RequestServices.GetRequiredService<ICurrentTenant>(); _cacheKey = KeyPattern.ToApiCacheKey(context.HttpContext.Request, _currentTenant.Id); _redisClient.KeyDelete(_cacheKey); } }
标签:缓存,return,05,API,context,过滤器,HttpContext,public 来源: https://www.cnblogs.com/hjwcore/p/16513873.html