首页 > 系统相关 >Asp.Net Core中利用过滤器控制Nginx的缓存时间

Asp.Net Core中利用过滤器控制Nginx的缓存时间

时间:2023-02-10 18:12:35浏览次数:47  
标签:Core 00 Asp 请求 Cache Nginx 缓存 Expires

前言

Web项目中很多网页资源比如html、js、css通常会做服务器端的缓存,加快网页的加载速度

一些周期性变化的API数据也可以做缓存,例如广告资源位数据,菜单数据,商品类目数据,商品详情数据,商品列表数据,公共配置数据等,这样就可以省去很多在服务端手动实现缓存的操作

最早资源缓存大部分都用Expires、Cache-Control或Etag实现的,我们可以在WebServer中统一设置响应头,或者指定规则单独设置

以上都是基于Http协议的缓存,如今很多WebServer,例如Nginx和阿里二次开发的Tengine,都是自己的一套缓存实现,通过独有的响应头参数(X-Accel-Expires)来识别控制缓存,优先级是大于Http协议那些的

通常Nginx都是作为代理服务器,反向代理多台源服务器,如果开启了缓存,二次请求到了Nginx就会直接响应给客户端了,能减轻源服务器的压力

本文主要是基于 X-Accel-Expires 来实现缓存的,前提是在Nginx中已经配置了Proxy Cache规则

 

Nginx的缓存原理

1. 这是资源访问路径,通过Nginx反向代理多个源服务器,Nginx中配置了缓存,第二次访问到了Nginx就直接返回了,不会再到后面的源服务器

2. 常见的Http缓存响应头设置有以下几种,其中Etag和Last-Modified是组合使用的,X-Accel-Expires是Nginx独有的参数,优先级高于其他几个设置,值的单位是秒,0为不生效

Nginx缓存识别优先级如下

3. Nginx实现缓存的原理是把Url和相关参数,通过自定义组合作为Key,并使用MD5算法对Key进行哈希,把响应结果存到硬盘上的对应目录,支持通过命令清除缓存

具体可以参考以下文章,非常详细:

https://www.nginx.com/blog/nginx-high-performance-caching/

https://czerasz.com/2015/03/30/nginx-caching-tutorial/

 

代码实现

以下是通过过滤器实现控制该参数,支持在Controller或Action上传入滑动时间,或者固定时间,灵活控制缓存时间

    /// <summary>
    /// 配合nginx缓存
    /// </summary>
    [AttributeUsageAttribute(AttributeTargets.Class | AttributeTargets.Method,AllowMultiple = false)]
    public class NginxCacheFilterAttribute : Attribute, IAsyncActionFilter
    {
        /// <summary>
        /// 构造函数
        /// </summary>
        public NginxCacheFilterAttribute() { }
        
        /// <summary>
        /// 固定时间格式正则,例如:00:00 、10:30
        /// <summary>
        static Regex reg = new Regex(@"^(\d{1,2}):(\d{1,2})$",RegexOptions.IgnoreCase);

        /// <summary>
        /// 缓存清除固定时间,new string[] { "00:00", "10:00", "14:00", "15:00" }
        /// </summary>
        public string[] MustCleanTimes { get; set; }

        /// <summary>
        /// 缓存清除滑动时间,默认 300 (5分钟)
        /// </summary>
        public int Period { get; set; } = 300;

        /// <summary>
        /// 请求头变量
        /// </summary>
        const string X_Accel_Expires = "X-Accel-Expires";
        const string ETag = "ETag";
        const string Cache_Control = "Cache-Control";

        /// <summary>
        /// 过滤器执行
        /// </summary>
        /// <param name="context"></param>
        /// <param name="next"></param>
        /// <returns></returns>
        public Task OnActionExecutionAsync(ActionExecutingContext context,ActionExecutionDelegate next)
        {
            //非GET请求,不设置nginx缓存头
            if (context.HttpContext.Request.Method.ToUpper() != "GET") {
                return next.Invoke();
            }
            var response = context.HttpContext.Response;
            //判断固定时间
            if (MustCleanTimes != null && MustCleanTimes.Length > 0) {
                var nowTime = DateTime.Now;                  //当前时间
                var nowYmd = nowTime.ToString("yyyy-MM-dd"); //当前日期
                List<DateTime> cleanTimes = new List<DateTime>();
                foreach (var time in MustCleanTimes) {
                    if (reg.IsMatch(time) && DateTime.TryParse($"{nowYmd} {time}",out DateTime _date)) {
                        //已超时的推到第二天,例如设置的是00:00,刷新时间就应该是第二天的00:00
                        if (_date < nowTime)
                            cleanTimes.Add(_date.AddDays(1));
                        else
                            cleanTimes.Add(_date);
                    }
                }
                if (cleanTimes.Count > 0) {
                    var nextTime = cleanTimes.OrderBy(o => o).FirstOrDefault(); //下次刷新时间
                    var leftSeconds = nextTime.Subtract(nowTime).TotalSeconds;  //下次刷新剩余秒数
                    if (leftSeconds >= 0 && leftSeconds < Period)
                        Period = (int)leftSeconds;
                }
            }

            //添加X_Accel_Expires
            if (response.Headers.ContainsKey(X_Accel_Expires)) {
                response.Headers.Remove(X_Accel_Expires);
            }
            response.Headers.Add(X_Accel_Expires,Period.ToString());

            //添加ETag
            if (response.Headers.ContainsKey(ETag)) {
                response.Headers.Remove(ETag);
            }
            response.Headers.Add(ETag,new System.Net.Http.Headers.EntityTagHeaderValue($"\"{DateTime.Now.Ticks.ToString()}\"",true).ToString());

            //移除Cache-Control
            response.Headers.Remove(Cache_Control);

            return next.Invoke();
        }
    }

 具体的使用方式如下:

1. 全局用法,全局Api都是设置的默认缓存时间,不需要缓存的Api在Controller或Action上单独设置Period=0即可

//在Stratup中全局添加过滤器       
public void ConfigureServices(IServiceCollection services)
{
    services.AddControllers(config => {
        config.Filters.Add<NginxCacheFilterAttribute>();
    });
}


/// <summary>
/// 设置滑动时间
/// Period=0为不生效
/// </summary>
/// <returns></returns>
[HttpGet]
[NginxCacheFilter(Period = 0)]
public HttpResponseMessage TestCache1()
{
    return new HttpResponseMessage() { StatusCode = System.Net.HttpStatusCode.OK };
}

 2. 局部用法

/// <summary>
/// 设置滑动时间
/// 30秒后自动过期
/// </summary>
/// <returns></returns>
[HttpGet]
[NginxCacheFilter(Period = 30)]
public HttpResponseMessage TestCache1()
{
    return new HttpResponseMessage() { StatusCode = System.Net.HttpStatusCode.OK };
}

/// <summary>
/// 设置固定时间
/// 例如:9点第一次请求,一直缓存到10点失效,12点第一次请求,一直缓存到15点失效
/// </summary>
/// <returns></returns>
[HttpGet]
[NginxCacheFilter(MustCleanTimes = new[] { "10:00","15:00","22:00" })]
public HttpResponseMessage TestCache2()
{
    return new HttpResponseMessage() { StatusCode = System.Net.HttpStatusCode.OK };
}

 

具体效果

1. 我们第一次请求接口,返回200状态码,Nginx在响应头上会返回X-Cache:MISS,代表缓存未命中

2. 第二次请求,会返回304状态码,Nginx在响应头上会返回 X-Cache:HIT,代表已经命中缓存

3. 我们开启Chrome调试中的Disable Cache,这样所有请求的请求头中都会设置 Cache-Control: no-cache,再刷新下接口看下

发现接口返回200状态码,Nginx在响应头上会返回X-Cache:EXPIRED,说明缓存已过期,已从源服务器返回了数据,也说明通过请求头设置Cache-Control为no cache是可以跳过缓存的

更多含义:

 

高性能用法:

proxy_cache_lock:缓存锁

proxy_cache_lock_timeout:缓存锁过期时间

如果给缓存规则设置了proxy_cache_lock,那么该规则下同时进来多个同一个Key的请求,只会有一个请求被转发到后面的源服务器,其余请求会被等待,直到源服务器的内容被成功缓存

可以配合设置proxy_cache_lock_timeout,设置一个缓存锁的过期时间,这样其余请求如果等待超时了,也会被释放请求到后面的源服务器

通过这两个参数的组合使用,可以有效避免同一个请求大量打入时,瞬间压垮后面的源服务器

 

原创作者:Harry

原文出处:https://www.cnblogs.com/simendancer/articles/17109964.html

标签:Core,00,Asp,请求,Cache,Nginx,缓存,Expires
From: https://www.cnblogs.com/simendancer/p/17109964.html

相关文章

  • nginx在windows的配置
    1、nginx-service.exe文件https://repo.jenkins-ci.org/releases/com/sun/winsw/winsw下载服务封装软件nginx-service.exe由winsw重命名而来2、nginx-service......
  • NGINX多用途集景
    Nginx一网打尽:动静分离、压缩、缓存、黑白名单、跨域、高可用、性能优化...Java知音 2023-01-1710:05 发表于山西干货!文章有点长,建议先收藏引言一、性能怪兽......
  • Nginx 实现 Rewrite 跳转
    上一篇文章对Nginx的Location配置进行了讲解,本篇主要对于Nginx中的Rewrite跳转进行讲解。因为目前很多工作前端开发都会选择使用Nginx作为反向代理服务器,但是平时业务需要......
  • 【HMS Core】一张图片带你玩转机器学习服务
    ​1、介绍总览CloudDB(云数据库)是一款端云协同的数据库产品,提供端云数据的协同管理、统一的数据模型和丰富的数据管理API接口等能力。在保证数据的可用性、可靠性、一致......
  • 【HMS Core】推送服务与运动健康服务热门问题汇总
    【推送服务】问题描述:申请自分类权益后,推送数量依旧变少分析原因:自分类权益后,如果没有在每条消息都正确携带importance和category字段会导致频控,所以部分的消息被缓存,没......
  • Dockerize ASP Classic on IIS
    DockerizeASPClassiconIISAskQuestionAsked 4years,1monthagoActive 9monthsagoViewed 9ktimes 2216Microsofthasbeeninvesti......
  • Consul+Ocelot+Polly在.NetCore中使用(.NET5)-网关Ocelot+Consul
     Consul+Ocelot+Polly在.NetCore中使用(.NET5)-Ocelot+Polly缓存、限流、熔断、降级Consul+Ocelot+Polly在.NetCore中使用(.NET5)-网关Ocelot+Consul微服务网关Ocelot......
  • Consul+Ocelot+Polly在.NetCore中使用(.NET5)-Ocelot+Polly缓存、限流、熔断、降级
    Consul+Ocelot+Polly在.NetCore中使用(.NET5)-Ocelot+Polly缓存、限流、熔断、降级Consul+Ocelot+Polly在.NetCore中使用(.NET5)-网关Ocelot+Consul微服务网关Ocelot加入......
  • .net core概述
    .netcore入门.netcore概述.Netcore中的代码绝大部分是从.netframework中迁移或者改造过来的。ASP.NetCore是一个跨平台的,高性能的开源框架,有以下优点:模块化开发,......
  • docker搭建vue+nginx部署
    Vue+nginx部署1.首先安装ningx镜像 2.将nginx关键目录映射到本机  首先在本机创建nginx的文件存储目录       www:nginx存储网站网页的目录......