zoukankan      html  css  js  c++  java
  • 记一次完整的asp.net-mvc页面优化过程

    最近在重构一个以前团队开发留下的MVC项目,项目结构堪称混乱,问题多多,但今天说的是页面打开速度的问题。项目中包括web后台系统,几乎随便点一个页面都要盯着白屏等待2-5秒之久,体验很差。通过对页面性能逐步的分析和判断,并做相应优化,最终页面打开速度在1秒内,发布后打开速度约60ms。

    一、项目结构

    后台系统开发使用了典型的微软ASP.Net MVC框架,但是版本略旧,.Net Framework4,MVC4,EF5,基本没有采用其它的技术工具,业务逻辑大致都混乱的分布在Controller和Model层。习惯了ABP框架,相比之下这个就显得简陋了。至于View层,摘要中提到了白屏,可以知道前端页面使用多页开发,后台服务器渲染,正是Razor模板引擎,版本2.0。

    登录后台系统,随便点击左侧菜单,右侧打开页面,大概需要白屏等待2-5秒。咦,怎么会如此之慢?!

    于是基于一个典型的页面,开始进行监测分析。我选择了[所有用户]菜单,页面打开速度在3秒左右,页面结构和功能都相对简单,上面是一个搜索条件区域,可以按照姓名部门角色等条件组合搜索,下面就是用户列表和分页,展示大约10个字段,大致示意图如下(虽然是测试阶段,但数据真实,为避免泄露客户信息,而且页面结构简单易于描述,所以不做截屏展示)。

    二、问题分析

    系统架构简单,页面的大概执行周期如下图示:

     

    从中基本推断出耗时操作可能发生在

    1. 网络传输
    2. 数据库操作
    3. 业务逻辑执行
    4. Razor渲染
    5. 浏览器页面渲染

    用排除法,我首先检查了客户端浏览器,Web服务器,数据库服务器的网络环境都正常,页面使用Bundle合并压缩了JS,CSS,数据库操作都是简单的查询,所以主要跟传输耗时相关的1,2两项可以先排除。

    下一步,我检查了业务执行逻辑,也是很简单的根据搜索条件查询数据,并返回View,不存在很复杂的逻辑调用,3项排除。

    然后,页面渲染也没有特殊之处,虽然也没有特别优化,但这种简单的页面渲染耗时应该大概在50ms,5项也可以排除了。

    引用福尔摩斯的一句话“排除所有的不可能,剩下的即使再不可能,那也是真相。”,我已久不用Razor开发多页应用,虽不是很了解,但以前记得Razor的效率也没有这么低啊,一个渲染模板引擎的执行至于耗费2秒吗?

    好吧,无论如何目前这项是最可疑的,先用Log4Net查看下视图渲染用了多少时间,证明下我的猜想。

    ActionFilterAttribute中监测记录View用时经典代码:

     1         //View生成时间监控
     2         public override void OnResultExecuting(ResultExecutingContext filterContext)
     3         {
     4             MonitorLog MonLog = filterContext.Controller.ViewData[Key] as MonitorLog;
     5             MonLog.ExecuteStartTime = DateTime.Now;
     6         }
     7         public override void OnResultExecuted(ResultExecutedContext filterContext)
     8         {
     9             MonitorLog MonLog = filterContext.Controller.ViewData[Key] as MonitorLog;
    10             MonLog.ExecuteEndTime = DateTime.Now;
    11             LoggerHelper.Monitor(MonLog.GetLoginfo(MonitorLog.MonitorType.View));
    12             filterContext.Controller.ViewData.Remove(Key);
    13         }

    结果似乎印证了我的猜想,如下图。

    渲染出视图居然耗费了2.6秒之多!是因为Razor低版本存在的问题吗?有配置不合理吗?Razor本身就这么低效吗?瞬间随之几个想法又冒了出来。

    三、改进

    随后进一步查询了关于Razor的技术信息,做了下面两个方面的改进。

    1.在global.asax文件Application_Start()方法中设置仅使用Razor视图引擎。由于默认MVC会首先加载WebForm视图引擎,而本项目中仅用Razor引擎。

    1 //只使用Razor引擎
    2 ViewEngines.Engines.Clear(); 
    3 ViewEngines.Engines.Add(new RazorViewEngine());

    2.设置Web.Config,设置debug模式为false。

    1 <system.web>
    2     <compilation debug="false" targetFramework="4.0" />
    3 ......

    这会带来几个方面的优化,如视图文件地址缓存,启用请求超时,Bundle功能生效等。

    这些也似乎没有什么好优化的,然后,我再次运行页面,发现打开时间几乎没有减少,WTF?!

    四、继续改进

     再看监测日志,视图渲染仍然用去大概2秒以上,这其中必有蹊跷,不达目的不罢休,于是我又引入了性能监测工具,MiniProfiler。

     然后再次打开页面,看到红色的监测提示时,瞬间明白了问题所在。

    这个请求过程中sql请求占用90%以上的时间,居然使用了31条SQL,而且我打开看重复语句达20多条!

    按道理说,我在看业务逻辑代码时,如果代码中会产生这么多SQL调用,一眼就可以看出来了,然而并没有,而且日志记录了时间耗费是计算在视图渲染中的。

    我恍然大悟,如果自己来写Razor渲染的cshtml代码,一定会将Model数据完善后,传过来直接使用,不可能在cshtml代码中再去请求数据,而这里cshtml中的代码,却在反复的去请求数据库数据。

    后台的Linq语句大概是db.Users.ToList(),cshtml页面Model为@model PagedList<Models.Users>,而嵌在cshtml中的除了 model.UserName这一类,还有大量的model.Employee.EmpNo,即有的用户有个内部员工身份,大量的延迟加载导航属性导致反复的数据库访问请求。

    那么到这里,问题也就明晰了,在后台的Linq语句中,应当把这些导航属性一次性Include进来,如.Include(x=>x.Employee),还有.Include(x=>x.Roles)等等,修改后再来看打开页面的监测结果。

     

    可以看到用时从2769ms减少到656ms,页面打开用时较之前减少了四分之三,控制在1秒之内,发布之后页面打开用时约60ms,sql仅仅用了5条便完成业务逻辑,这才是asp.net mvc最起码该有的样子。

  • 相关阅读:
    HashMap扩容后是否需要rehash?
    为什么我们在定义HashMap的时候,就指定它的初始化大小呢
    HashMap是如何进行扩容的?
    【华为云技术分享】如何使用pyCharm与ModelArts公有云服务联动开发,快速且充分地利用云端GPU计算资源
    数据库“意外失联”?华为云DRS异地多活灾备为您支招
    【华为云技术分享】为什么说物联网平台是城市数字化的必备底座
    【华为云技术分享】HBase与AI/用户画像/推荐系统的结合:CloudTable标签索引特性介绍
    【华为云技术分享】云小课 | WAF反爬虫“三板斧”:轻松应对网站恶意爬虫
    【华为云技术分享】云小课 | 迁移第三方云厂商数据至OBS,两种方式任你选
    【华为云技术分享】网络场景AI模型训练效率实践
  • 原文地址:https://www.cnblogs.com/jiujiduilie/p/9419460.html
Copyright © 2011-2022 走看看