• 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/

  • 相关阅读:
    java api 中的设计模式之组合(composite)
    Java api 中设计模式之适配器模式(Adapter)
    win 7 C盘清理
    centos 与 redhat 以及 mysql 与 Oracle
    tsung生成报表时报错
    对MySQL的死连接Sleep的进程的来源研究[转]
    商派ecstore后台配置微信支付应该注意的问题
    商派crm的内存计算组件配置mysql5.6的问题
    php-curl无法编译
    ecstore目录系统预占字符
  • 原文地址:https://www.cnblogs.com/jiujiduilie/p/8371378.html
Copyright © 2020-2023  润新知