前言

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即可

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

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

Asp.Net Core中利用过滤器控制Nginx的缓存时间的更多相关文章

  1. Asp.Net Core中利用Seq组件展示结构化日志功能

    在一次.Net Core小项目的开发中,掌握的不够深入,对日志记录并没有好好利用,以至于一出现异常问题,都得跑动服务器上查看,那时一度怀疑自己肯定没学好,不然这一块日志不可能需要自己扒服务器日志来查看 ...

  2. Asp.Net Core 中利用QuartzHostedService 实现 Quartz 注入依赖 (DI)

    QuartzHostedService  是一个用来在Asp.Net Core 中实现 Quartz 的任务注入依赖的nuget 包: 基本示例如下: using System; using Syst ...

  3. ASP.NET Core 中的SEO优化(4):自定义视图路径及主题切换

    系列回顾 <ASP.NET Core 中的SEO优化(1):中间件实现服务端静态化缓存> <ASP.NET Core 中的SEO优化(2):中间件中渲染Razor视图> < ...

  4. ASP.NET Core中的Http缓存

    ASP.NET Core中的Http缓存 Http响应缓存可减少客户端或代理对web服务器发出的请求数.响应缓存还减少了web服务器生成响应所需的工作量.响应缓存由Http请求中的header控制. ...

  5. 第十五节:Asp.Net Core中的各种过滤器(授权、资源、操作、结果、异常)

    一. 简介 1. 说明 提到过滤器,通常是指请求处理管道中特定阶段之前或之后的代码,可以处理:授权.响应缓存(对请求管道进行短路,以便返回缓存的响应). 防盗链.本地化国际化等,过滤器用于横向处理业务 ...

  6. ASP.NET Core中的依赖注入(1):控制反转(IoC)

    ASP.NET Core在启动以及后续针对每个请求的处理过程中的各个环节都需要相应的组件提供相应的服务,为了方便对这些组件进行定制,ASP.NET通过定义接口的方式对它们进行了"标准化&qu ...

  7. ASP.NET Core中使用自定义MVC过滤器属性的依赖注入

    除了将自己的中间件添加到ASP.NET MVC Core应用程序管道之外,您还可以使用自定义MVC过滤器属性来控制响应,并有选择地将它们应用于整个控制器或控制器操作. ASP.NET Core中常用的 ...

  8. 在asp.net web api中利用过滤器设置输出缓存

    介绍 本文将介绍如何在asp.net web api中利用过滤器属性实现缓存. 实现过程 1,首先在web.config文件下appsettings下定义“CacheEnabled”和“CacheTi ...

  9. 避免在ASP.NET Core中使用服务定位器模式

    (此文章同时发表在本人微信公众号"dotNET每日精华文章",欢迎右边二维码来关注.) 题记:服务定位器(Service Locator)作为一种反模式,一般情况下应该避免使用,在 ...

  10. 第十四节:Asp.Net Core 中的跨域解决方案(Cors、jsonp改造、chrome配置)

    一. 整体说明 1. 说在前面的话 早在前面的章节中,就详细介绍了.Net FrameWork版本下MVC和WebApi的跨域解决方案,详见:https://www.cnblogs.com/yaope ...

随机推荐

  1. 2021-2022 ICPC, NERC, Northern Eurasia Onsite (Unrated, Online Mirror, ICPC Rules, Teams Preferred) J. Job Lookup

    题意 n个节点,n<=200,你需要构造这n个几点成为一棵树,并且这棵树的中序遍历为1-n; 你构造树的节点之间的最短路构成一个n×n的最短距离矩阵d: 同时给你n×n的权重矩阵c:最最小的Σd ...

  2. 关于li标签的相关css属性

    1.让li前面的序号变成空心圆 list-style-type: circle;   2.让li前面的序号在div里面 list-style-position: inside;   3.改变li前面的 ...

  3. 删除redis对应key的缓存

    [root@zhyly-pre-002 ~]# /usr/local/redis/bin/redis-cli -p 6379 #登录redis 127.0.0.1:6379> auth 'Red ...

  4. Windows Server 2019 安装 Oracle 19C RAC(VMWare虚拟机环境)

    软件 Windows Server 2019 Standard Oracle 19C Oracle Grid 19 VMware Workstation 16 规划 共享存储,使用Windows Se ...

  5. Go语言核心36讲16----接口

    你好,我是郝林,今天我们来聊聊接口的相关内容. 前导内容:正确使用接口的基础知识 在Go语言的语境中,当我们在谈论"接口"的时候,一定指的是接口类型.因为接口类型与其他数据类型不同 ...

  6. 核磁共振成像学习笔记——从FID信号到K空间

    在理想磁场环境下(没有不存在场不均匀性),对于一个没有梯度场的方块. 此时,RF pulse的两路正交信号(相位差为90°)对此方块进行激发,然后收取信号,我们可以得到由此方块产生的FID信号. 设此 ...

  7. 关于在linux测试启动盘命令(qemu的使用)

    前言 在平时使用电脑的过程中,难免有更换系统或维护系统的需求,这个时候就需要一个启动盘来进行帮忙了,但启动盘制作是否成功没次都重启电脑是十分浪费时间的 在windows中,制作过pe盘,或者其他的系统 ...

  8. ArcObjects SDK开发 005 ArcObjects SDK中的插件式架构

    1.什么是插件式架构 插件式架构设计中主要包括三个重要部分,宿主.插件协议以及插件实现.宿主是指使用插件的部分,该模块可以是一个类,也可以是多个接口和类组成的模块.插件协议是指宿主与插件之间的协议,宿 ...

  9. 5V降压转3.3V,5V转3V电路图芯片

    5V降压转3.3V和3V都是低压,两个之间的压差效率,所以效率和工作温度这块都会比较优秀,输入和输出的最低压差外是越小越好. 1, 如果电流比较小,可以用LDO: PW6566 系列是使用 CMOS ...

  10. 【Java难点攻克】「NIO和内存映射性能提升系列」彻底透析NIO底层的内存映射机制原理与Direct Memory的关系

    NIO与内存映射文件 Java类库中的NIO包相对于IO包来说有一个新功能就是 [内存映射文件],在业务层面的日常开发过程中并不是经常会使用,但是一旦在处理大文件时是比较理想的提高效率的手段,之前已经 ...