博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
ASP.NET MVC ETag & Cache等优化方法
阅读量:5926 次
发布时间:2019-06-19

本文共 5979 字,大约阅读时间需要 19 分钟。

背景

最近有一个项目是用SmartAdmin + Jquery + EasyUI 一个ASP.NET MVC5的项目,一直存在一个性能问题,加载速度比较慢,第一次加载需要(在没有cache的情况下)需要4-5秒完成全部的加载.

如下图就是用Chrome PageSpeed 测试的结果 

有几个非常重要的指标

Fist Contentfu Paint:第一次绘出页面,需要4秒,前4秒都是白屏,确实有点长

Fist Meaningfull Paint:第一次绘出有意义的内容,需要8.6秒,才出现可见的操作页面.

Eliminate render-blocking resources:阻塞加载资源文件,因为的项目在head中加载了jquery和css,因为有些代码必须先执行导致的

Remove unused css:存在大量的没用的css样式定义,这也很难避免.

分析一下原因

出现上述问题的主要原因,页面本身的大小,所有资源加起来超过3.2M,Jquery EasyUI的JS+css 就接近3M,另外页面里有嵌入了好几个PartialView,还有就是执行js的时间,EasyUI DataGrid需要从后台抓起数据并生成复杂的Dom结构这都需要时间.

一般的优化手段

Cache

第一想到的就是使用cache,单只能解决第二次访问的速度问题,对少有点用,我一般会这样做,设置的方法有

  • 添加outputcache 例如:

 

[OutputCache(Duration = 360, VaryByParam = "none")]public ActionResult Index() => this.View();

 

  • web.Config 添加对静态文件的缓存

 

压缩和合并资源文件

尽量减少资源文件的大小和请求次数,通常的做法就是使用BundleConfig.cs合并和压缩js,css文件.我现在使用bundleconfig.json配置代替System.Web.Optimization.配置灵活一点,如果使用bundleconfig.json 编译压缩还需要解决客户端更新的缓存的问题,我使用一下代码添加一个指纹标志

public class Fingerprint    {        public static string Tag(string rootRelativePath)        {            if (HttpRuntime.Cache[rootRelativePath] == null)            {                string absolute = HostingEnvironment.MapPath("~" + rootRelativePath);                DateTime date = File.GetLastWriteTime(absolute);                int index = rootRelativePath.LastIndexOf('/');                string result = rootRelativePath.Insert(index, "/v-" + date.Ticks);                HttpRuntime.Cache.Insert(rootRelativePath, result, new CacheDependency(absolute));            }            return HttpRuntime.Cache[rootRelativePath] as string;        }    }

ETag

ETags 是用于 Web 缓存验证的工具,允许有条件的客户端请求。通过 ETags,浏览器可以判断某项资源是否被需要。如果不需要,浏览器就不会向 Web 服务器发送请求,从而最小化请求数量。配置方法

  • 全局方案,自定义一个HttpModule
public class ETagHttpModule : IHttpModule  {    #region IHttpModule Members    void IHttpModule.Dispose()    {      // Nothing to dispose;     }    void IHttpModule.Init(HttpApplication context)    {      context.BeginRequest += new EventHandler(context_BeginRequest);      WebPageHttpHandler.DisableWebPagesResponseHeader = true;    }    #endregion    void context_BeginRequest(object sender, EventArgs e)    {      HttpApplication app = sender as HttpApplication;      //if (app.Request.CurrentExecutionFilePath.EndsWith("/") || app.Request.CurrentExecutionFilePath.EndsWith(".cshtml"))      //{        app.Response.Filter = new ETagStream(app.Response, app.Request);      //}    }    #region Stream filter    public class ETagStream : MemoryStream    {      private HttpResponse _response = null;      private HttpRequest _request;      private Stream _filter = null;      public ETagStream(HttpResponse response, HttpRequest request)      {        _response = response;        _request = request;        _filter = response.Filter;      }      private string GetToken(Stream stream)      {        var checksum = new byte[0];        checksum = MD5.Create().ComputeHash(stream);        return Convert.ToBase64String(checksum, 0, checksum.Length);      }      public override void Write(byte[] buffer, int offset, int count)      {        var data = new byte[count];        Buffer.BlockCopy(buffer, offset, data, 0, count);        var token = GetToken(new MemoryStream(data));        var clientToken = _request.Headers["If-None-Match"];        if (token != clientToken)        {          _response.AddHeader("ETag", token);          _filter.Write(data, 0, count);        }        else        {          _response.SuppressContent = true;          _response.StatusCode = 304;          _response.StatusDescription = "Not Modified";          _response.AddHeader("Content-Length", "0");        }      }    }    #endregion  }
  • Action页面级

  

public class ETagAttribute : ActionFilterAttribute  {    public override void OnActionExecuting(ActionExecutingContext filterContext) => filterContext.HttpContext.Response.Filter = new ETagFilter(filterContext.HttpContext.Response, filterContext.RequestContext.HttpContext.Request);  }  public class ETagFilter : MemoryStream  {    private HttpResponseBase _response = null;    private HttpRequestBase _request;    private Stream _filter = null;    public ETagFilter(HttpResponseBase response, HttpRequestBase request)    {      _response = response;      _request = request;      _filter = response.Filter;    }    private string GetToken(Stream stream)    {      var checksum = new byte[0];      checksum = MD5.Create().ComputeHash(stream);      return Convert.ToBase64String(checksum, 0, checksum.Length);    }    public override void Write(byte[] buffer, int offset, int count)    {      var data = new byte[count];      Buffer.BlockCopy(buffer, offset, data, 0, count);      var token = GetToken(new MemoryStream(data));      var clientToken = _request.Headers["If-None-Match"];      if (token != clientToken)      {        _response.AddHeader("ETag", token);        _filter.Write(data, 0, count);      }      else      {        _response.SuppressContent = true;        _response.StatusCode = 304;        _response.StatusDescription = "Not Modified";        _response.AddHeader("Content-Length", "0");      }    }     }

  

//[OutputCache(Duration = 360, VaryByParam = "none")][ETag]public ActionResult Index() => this.View();

效果图,回发的字节数确实减少了很多,单响应时间差不多,不是很明显.

总结

优化方案有很多,但是感觉效果都不是很理想,要做到极致的用户体验,可能真的要抛弃Jquery,EasyUI,这类肥大又复杂的类库.

问题

另外大家有没有非常好用又简单的方法解决初始加载白屏的问题,我试过用js preloading图层动画,但是效果还是不理想.但看过一些网址和APP做的效果非常好,不知道具体是如何实现的,在Asp.net mvc环境下能不能用

参考文章

(ASP.NET MVC 应用提速的十种方法)

 

转载于:https://www.cnblogs.com/neozhu/p/10605929.html

你可能感兴趣的文章
JAVA特性-跨平台/面向对象
查看>>
利用Win10计划任务 + 弹窗,提醒你自己
查看>>
《php和mysql web开发》读书笔记
查看>>
第二章 生成、打包、部署和管理应用程序及类型
查看>>
Generate Parentheses
查看>>
括号配对问题2
查看>>
C#性能优化实践
查看>>
[HTML/CSS]display:none和visibility:hidden的区别
查看>>
Xcode导入第三方库
查看>>
css required,focus,valid和invalid介绍
查看>>
C# arcengine 由FeatureClass生成TIN
查看>>
Hibernate体系结构(入门)
查看>>
ios 导航栏按钮添加与隐藏
查看>>
大数据应用分类
查看>>
linux 下查看网卡工作速率
查看>>
我的简历
查看>>
《几何与代数导引》习题1.34.3
查看>>
《几何与代数导引》例2.7.2
查看>>
结对开发Ⅱ——二维数组求和最大的子数组
查看>>
《你的灯亮着吗》读书笔记3
查看>>