ABP給WebApi添加性能分析組件Miniprofiler


在ABP的WebApi中,對其性能進行分析監測是很有必要的。而悲劇的是,MVC項目中可以使用的MiniProfiler或Glimpse等,這些都不支持WebApi項目,而且WebApi項目通常也沒有界面,不能進行性能分析的交互。在上一篇教程中,通過集成SwaggerUI解決了界面的問題。在這篇文章中,我們就來一步步實現為WebApi項目集成Miniprofiler。集成后,我們可以監控EF執行效率,執行語句,頁面執行時間等,這些結果將以很友好的方式顯示在界面上。

問題分解

本質上,集成Miniprofiler-Swagger可以分解為三個問題:

  1. 怎樣監測一個WebApi項目的性能。
  2. 將性能分析監測信息從后端發送到UI。
  3. 在UI顯示分析監測結果。

一、分析WebApi項目性能

安裝Miniprofiler
在我們准備做Miniprofiler集成前,先思考一下Miniprofiler在監測MVC項目時是怎么做的,以便借鑒一下實現思路。當一個控制器執行后,Miniprofiler通過一個guid跟蹤請求執行的每一步的時間。執行活動完成后,跟蹤監測結果暫存在內存中。然后Miniprofiler通知其js模塊有一個新動作執行完成了。這使得其程序終端發出調用,請求獲取實時的結果。現在的問題是這是一個MVC終端,幸運的是在WebApi項目中構造這樣一個終端也很容易。

  1. 首先安裝Miniprofiler
    • Install-Package Miniprofiler。
    • 我們只需要Miniprofiler core,不需要Miniprofiler.mvc,但是注意一定要安裝V3之后的版本。
    • ABP中,安裝在Web項目和WebApi中。
  2. Install-Package Microsoft.AspNet.Mvc
    • ABP中,Web項目本身就有了,WebApi項目不需要安裝。
  3. 將以下代碼添加到web.config中的<handlers>節下。
<add name="MiniProfiler" path="mini-profiler-resources/*" verb="*" 
type="System.Web.Routing.UrlRoutingModule" resourceType="Unspecified" 
preCondition="integratedMode" />
  1. 將以下代碼添加到Global.asax
protected void Application_BeginRequest()
{
    MiniProfiler.Start();
}
protected void Application_EndRequest()
{
    MiniProfiler.Stop();
}
  1. 測試一下所做的是否正確
    • 重新編譯運行程序,在swagger中執行一個動作
    • 訪問http://localhost/mini-profiler-resources/results
    • 正常執行結果類似如下

二、將分析監測信息從后台發送到UI

將Miniprofiler元數據注入swashbuckle
在MVC項目中,我們通常在razor視圖中添加一個輔助方法RenderInclude,從而注入一些代碼執行和渲染分析監測結果盒子。然而在WebApi中是沒有頁面可以注入的,所以讓我們來點創新吧。
我們觀察下RenderInclude的輸出,可以看到Miniprofiler注入了一個異步的腳本標簽。這個標簽從Miniprofiler終端下載了一個js腳本,然后腳本運行展示出分析監測結果。
我們知道在swagger中可以注入js腳本,例如我們上節教程中注入的漢化js文本,然后腳本會在swaggerUI上運行。這就足夠我們進行上面的步驟了。

  1. 首先我們來添加一個swagger的IDocumentFilter。
    • 這個filter在每次swagger頁面加載時都會執行。
public class InjectMiniProfiler : IDocumentFilter
    {
        public void Apply(SwaggerDocument swaggerDoc, SchemaRegistry schemaRegistry, IApiExplorer apiExplorer)
        {
            swaggerDoc.info.contact = new Contact()
            {
                name = MiniProfiler.RenderIncludes().ToHtmlString()
            };
        }
    }
* 我們把Miniprofiler的渲染腳本標簽賦給了swaggerDoc的聯系人名稱,真是太有才了 ^_^ 
  1. 反注釋swaggerconfig.cs中的documentfilter代碼,並將其指向剛才創建的filter
    c.DocumentFilter<InjectMiniProfiler>();
  2. 編譯運行並刷新swagger頁面,可以看到創建人是 script async... _
  3. 我們已經獲得了在客戶端需要的腳本。現在我們只需要用js將其改進一下。

三、在UI上顯示分析監測結果

在swagger頁面上運行Miniprofiler Js

  1. 添加js文件SwaggerUiCustomization.js
  2. 設置js文件屬性為嵌入的資源,以確保其總是被拷貝到輸出目錄
  3. Js代碼如下,這段代碼主要是獲取我們之前注入的腳本文本(使用JQuery),創建新的腳本標簽,並附加到DOM,腳本會隨后執行。
//Create a mini profiler script tag with the right properites 
var MiniProfiler = $('#api_info > div:nth-child(3)').text();
const attributes = [
    'src', 'data-version', 'data-path', 'data-current-id', 'data-ids',
    'data-position', 'data-trivial', 'data-children', 'data-max-traces', 'data-controls',
    'data-authorized', 'data-toggle-shortcut', 'data-start-hidden', 'data-trivial-milliseconds'
];
var GetAttr = function (input, attributeName) {
    const myRegexp = attributeName + '="(.*?)"';
    const re = new RegExp(myRegexp, "g");
    const match = re.exec(input);
    return match[1];
}
var s = document.createElement("script");
s.type = "text/javascript";
s.id = "mini-profiler";
s.async = true; 
for (var i = 0; i < attributes.length; i++) {
    var element = attributes[i];
    s.setAttribute(element, GetAttr(MiniProfiler, element));
}
document.body.appendChild(s);
// Remove injected tag from view 
$('#api_info > div:nth-child(3)').text('');
  1. 修改swaggerconfig.cs的InjectJavaScript,將上面創建的js注入。
string resourceName2 = thisAssembly.FullName.Substring(0, thisAssembly.FullName.IndexOf(",")) + ".Scripts.swaggerui.SwaggerUiCustomization.js";
 c.InjectJavaScript(thisAssembly, resourceName2);
  1. 重新編譯運行,刷新swagger頁面,可以看到注入的文本從swagger頁面上消失了,而Miniprofiler彈出層出現在頁面左上角。

四、實時刷新監測結果

蛋疼的是,我們還沒有完全搞定。你可以執行一個Swagger接口,然后發現Miniprofiler並未跟着更新結果,那上面這一切都沒有意義了。幸運的是,Miniprofiler在MVC網站中被設計為同樣可以使用AJAX調用。因此,我們可以利用這一點進行改進。掃讀Miniprofiler的JS代碼,我們發現它可以監聽angular頁面應用的xhr對象。

  1. 我們往SwaggerUiCustomization.js文件中添加代碼window.angular=true;,假裝我們使用angular。
    • 這就是我們要使用MiniprofilerV3.2版本的原因。V3.0版本在xhr監聽上有個bug,會導致stackoverflow錯誤。如果你的瀏覽器控制台拋出stackoverflow異常,那你應該再次檢查是否使用了正確的Miniprofiler版本。
  2. 重新編譯運行程序,刷新Swagger頁面。
  3. 到此為止我們僅僅實現了可以讓Miniprofiler實時監聽新的請求調用。為了顯示出監測信息,還需要傳遞所監測的活動的ID列表。這個可以用另外一個filter輕松實現。
    • ABP項目由於存在Web項目,所以可以不必再按下面步驟處理。
  4. 在WebApi中新建Filter文件夾,創建WebApiProfilingActionFilter.cs。
    public class WebApiProfilingActionFilter : ActionFilterAttribute
    {
        public const string MiniProfilerResultsHeaderName = "X-MiniProfiler-Ids";

        public override void OnActionExecuted(HttpActionExecutedContext filterContext)
        {
            var MiniProfilerJson = JsonConvert.SerializeObject(new[] {MiniProfiler.Current.Id});
            filterContext.Response.Content.Headers.Add(MiniProfilerResultsHeaderName, MiniProfilerJson);
        }
    }
  1. WebApiConfig.cs中注冊這個Filterconfig.Filters.Add(new WebApiProfilingActionFilter());
    • ABP項目中應在WebApiModule中的Initialize()方法中,添加配置Configuration.Modules.AbpWebApi().HttpConfiguration.Filters.Add(new WebApiProfilingActionFilter());
  2. 重新編譯運行程序,刷新Swagger頁面,之后在每次執行接口后,會發現左上角的分析監測界面會實時增加一行結果。另外,在HTTP響應頭中會看見一個類似的記錄"x-MiniProfiler-ids": "[\"b9512f60-9e75-42f9-bdc1-597695e5b745\"]",
  3. 大功告成!再多添加幾個接口,試試效果吧~

五、總結

審視一遍最終的代碼,看起來很簡單,但是這個過程中是作了很多的嘗試和試錯,來讓swashbuckle和Miniprofiler集成起來,同時做了一些重構來減少代碼量,但是我對最終結果還是非常滿意的。如果swashbuckle能有更簡便的傳遞元數據的方法,那就更好啦。另外,我知道它支持定制前端頁面,但是在這里我們只是要顯示出監測分析結果就達到目的,所以只需要做一些調整,而不是從頭開始做一個頁面。
這些都實現后,我們就可以進一步使用所有的Miniprofiler插件了,譬如Entity framework profiling。
下一篇教程中,我們將把Miniprofiler EF6集成到項目中,以實現EF的監測分析。

參考鏈接:http://www.lambdatwist.com/webapi-profiling-with-miniprofiler-swagger/


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM