2011年9月26日 星期一

ASP.NET MVC 2 + MiniProfiler 偵測執行效能


有時候常常會遇到執行效能的問題,除了找尋讓執行效能延遲的原因,也希望能夠知道每個步驟與環節所耗費的時間,

這樣就可以知道哪些地方發生了問題,或是使用了錯誤的程式寫法而使得系統效能出現問題,

很多人會用的老方法就是在程式執行的開始與結束去記錄時間,然後再把時間做相減後得到一個執行的時間,

但是這會遇到很多個問題,

第一個會遇到的就是,開發時期可以這麼做,

但如果要部屬到測試機或是正式機前,必須要手忙腳亂的把這些用來偵測的程式給移除或是註解起來,

而且還有可能會有不小心遺漏的地方。

第二個會遇到的問題將事前項問題最後所提到的,如果你要偵測的地方很多,那麼你要去下偵測程式碼的地方也很多。

第三個會遇到的問題是,當你所開發的專案有切分很多層時,往往在畫面上就不容易去顯示除了顯示層以外其他層所要偵測的資料。

如果有一個工具可以解決以上的問題,那麼在系統開發就會很方便也更容易及早發現影響效能的地方。


MiniProfiler

這是一個提供給ASP.NET / ASP.NET MVC開發的事件偵測器,主要就是用來偵測執行的效能。

mvc-mini-profiler - A simple but effective mini-profiler for ASP.NET and WCF

github:https://github.com/SamSaffron/MiniProfiler

如果你是用VS2010來開發專案並且有安裝NuGet的話,可以用NuGet Packages Manager來安裝MiniProfiler,

image

本篇文章是以ASP.NET MVC2來開發,所以只需要安裝「MiniProfiler」,至於其他兩項則是用於ASP.NET MVC3,留待下一篇文章再做說明。

Nuget Gallery:http://nuget.org/List/Packages/MiniProfiler

PM> Install-Package MiniProfiler

 

安裝之後可以在專案的參考目錄中看到已經加入MvcMiniProfiler的參考。

image

 

再來就是對Global.asax做些修改:於Global.asax中加入以下的兩個方法

protected void Application_BeginRequest()
{
  if (Request.IsLocal)
  {
    MiniProfiler.Start();
  }
}
protected void Application_EndRequest()
{
  MiniProfiler.Stop();
}

 

再接下來就是修改ViewPage:例如我是直接加在Site.Master裡,

<%@ Master Language="C#" Inherits="System.Web.Mvc.ViewMasterPage" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
    <title><asp:ContentPlaceHolder ID="TitleContent" runat="server" /></title>
    <link href="../../Content/Site.css" rel="stylesheet" type="text/css" />
  <%: MvcMiniProfiler.MiniProfiler.RenderIncludes() %>
</head>

記得!是在<head>…</head>區塊中加入

<%: MvcMiniProfiler.MiniProfiler.RenderIncludes() %>

還有一點要記得,那就是專案一定要加入jQuery。

 

最後就是去對要偵測執行效能的地方去進行修改:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web.Mvc;
using MvcMiniProfiler;
using Test.Library;
using Test.Model;
using Test.Service;
namespace Lab_CheckBoxList.Controllers
{
  [HandleError]
  public class HomeController : Controller
  {
    readonly PostalCityService _cityService = new PostalCityService();
    public ActionResult Index()
    {
      var profiler = MiniProfiler.Current;
      using (profiler.Step("Home - Index"))
      {
        IQueryable<PostalCity> cities = _cityService.GetCollection();
        var infos = new List<CheckBoxListInfo>();
        foreach (var item in cities)
        {
          infos.Add(new CheckBoxListInfo
          (
            item.SORT.ToString(), 
            string.Concat(item.NAME, "-", item.SORT.ToString()), 
            false)
          );
        }
        ViewData["CheckBoxListOfCities"] = infos;
        return View();
      }
    }
  }
}

加入的方法很簡單,就是先建立一個MiniProfiler的物件實體,

var profiler = MiniProfiler.Current;

然後就是用Using(…){ …… } 去包起來,其中Step裡面的字串是要再偵測畫面中顯示的偵測區段名稱,

using (profiler.Step("Home - Index"))
{
    ...
    ...
}

來看看實際執行的畫面:可以看到在網頁的左上方出現一個標記時間的區塊,這個時間就是這個網頁所執行的時間。

image

 

點擊區塊後:可以看到剛才在Controller Action裡面所指定偵測的地方於執行時所耗費的時間。

image

 

剛剛偵測效能的區塊,我們還可以再去放置偵測區塊,以用來觀察更細微的地方:

public ActionResult Index()
{
  var profiler = MiniProfiler.Current;
  using (profiler.Step("Home - Index"))
  {
    IQueryable<PostalCity> cities;
    using (profiler.Step("Get Cities"))
    {
      cities = _cityService.GetCollection();
    }
    var infos = new List<CheckBoxListInfo>();
    using (profiler.Step("Generate CheckBoxList"))
    {
      
      foreach (var item in cities)
      {
        infos.Add(new CheckBoxListInfo
        (
          item.SORT.ToString(),
          string.Concat(item.NAME, "-", item.SORT.ToString()),
          false)
        );
      }
    }
    ViewData["CheckBoxListOfCities"] = infos;
    return View();
  }
}

 

執行畫面:

可以看到剛剛所增加的偵測區塊名稱。

image

點擊「Show trivial」後,可以看到所有的偵測區塊。

image

點擊「Show time with children」後,可以看到如果偵測區塊裡面還有子偵測區塊時,就會顯示其加總與子項目的個別執行時間。

image

 

如果說專案裡面是有分層的,例如資料存取層是一個專案,而Service層又是另一個專案,放置偵測區塊的方式也是一樣,

而在畫面上所顯示的執行時間,也是一樣會做加總與故別執行時間顯示。

image

 

看到這裡大致上就介紹差不多了,但或許有人看到「google code:mvc-mini-profiler」頁面上的一張圖而提出疑問,

image

為什麼會有「sql」字樣呢?

是的,MiniProfiler也可以偵測SQL,如果資料存取層是使用Linq to SQL或是ADO.NET Entity Framework的話,

還可以把自動產生的SQL Script給顯示出來,

但是……

我必須說,我在ASP.NET MVC 2的專案上面怎麼試都不行,而網路上的範例也都是以MVC3為主,

所以當我另外使用MVC3來開啟新專案並加入for MVC3的MiniProfiler後,原來顯示SQL的功能必須在MVC3才會有,

而且還必須對專案做點小修改,

所以……

就留待下一篇文章來做說明。

 

延伸閱讀:

Sam Saffron - Profiling your website like a true Ninja

Scott Hanselman - NuGet Package of the Week #9 - ASP.NET MiniProfiler from StackExchange rocks your world

MiniProfiler: Lightweight profiler for ASP.NET web applications

KingKong Bruce記事: ASP.NET MVC - 使用MVC MiniProfiler測試您的網頁跑多快

Jun1st Notes - 使用MiniProfiler来优化你EntityFramework的查询

 

以上

沒有留言:

張貼留言

提醒

千萬不要使用 Google Talk (Hangouts) 或 Facebook 及時通訊與我聯繫、提問,因為會掉訊息甚至我是過了好幾天之後才發現到你曾經傳給我訊息過,請多多使用「詢問與建議」(在左邊,就在左邊),另外比較深入的問題討論,或是有牽涉到你實作程式碼的內容,不適合在留言板裡留言討論,請務必使用「詢問與建議」功能(可以夾帶檔案),謝謝。