Web项目中很多网页资源比如html、js、css通常会做服务器端的缓存,加快网页的加载速度一些周期性变化的API数据也可以做缓存,例如广告资源位数据,菜单数据,商品类目数据,商品详情数据,商品列表数据,公共配置数据等,这样就可以省去很多在服务端手动实现缓存的操作,最早资源缓存大部分都用Expires、Cache-Control或Etag实现的,我们可以在WebServer中统一设置响应头,或者指定规则单独设置。
1. 这是资源访问路径,通过Nginx反向代理多个源服务器,Nginx中配置了缓存,第二次访问到了Nginx就直接返回了,不会再到后面的源服务器
/// <summary> /// 堆代码 www.duidaima.com /// 配合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(); } }具体的使用方式如下:
// 堆代码 www.duidaima.com //在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 }; }具体效果
如果给缓存规则设置了proxy_cache_lock,那么该规则下同时进来多个同一个Key的请求,只会有一个请求被转发到后面的源服务器,其余请求会被等待,直到源服务器的内容被成功缓存,可以配合设置proxy_cache_lock_timeout,设置一个缓存锁的过期时间,这样其余请求如果等待超时了,也会被释放请求到后面的源服务器。通过这两个参数的组合使用,可以有效避免同一个请求大量打入时,瞬间压垮后面的源服务器。