其他分享
首页 > 其他分享> > D_05 API缓存过滤器(aop方式实现缓存)

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