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

ABP给WebApi添加性能分析组件Miniprofiler的更多相关文章

  1. ASP.NET Core WebAPI中的分析工具MiniProfiler

    介绍 作为一个开发人员,你知道如何分析自己开发的Api性能么? 在Visual Studio和Azure中, 我们可以使用Application Insight来监控项目.除此之外我们还可以使用一个免 ...

  2. ABP给WebApi添加SwaggerUI,生成可交互接口文档

    在ABP模板项目中,通过SwaggerUI可以为我们的WebApi生成动态的可交互接口文档页面,从而可以很方便的测试调用我们的WebApi接口. 一.集成Swagger 右键项目YoYo.Web,打开 ...

  3. MiniProfiler性能分析工具— .Net Core中用法

    前言: 在日常开发中,应用程序的性能是我们需要关注的一个重点问题.当然我们有很多工具来分析程序性能:如:Zipkin等:但这些过于复杂,需要单独搭建. MiniProfiler就是一款简单,但功能强大 ...

  4. Centos下给PHP7添加Xhprof性能分析

    什么是 Xhprof?XHProf是facebook 开发的一个测试php性能的扩展,本文记录了在PHP应用中使用XHProf对PHP进行性能优化,查找性能瓶颈的方法. 它报告函数级别的请求次数和各种 ...

  5. 如何进行python性能分析?

    在分析python代码性能瓶颈,但又不想修改源代码的时候,ipython shell以及第三方库提供了很多扩展工具,可以不用在代码里面加上统计性能的装饰器,也能很方便直观的分析代码性能.下面以我自己实 ...

  6. MySQL性能分析(转)

    第一步:检查系统的状态 通过操作系统的一些工具检查系统的状态,比如CPU.内存.交换.磁盘的利用率.IO.网络,根据经验或与系统正常时的状态相比对,有时系统表面上看起来看空闲,这也可能不是一个正常的状 ...

  7. LoadRunner性能分析指标解释

    Transactions(用户事务分析) 用户事务分析是站在用户角度进行的基础性能分析. 1.Transation Sunmmary(事务综述) 对事务进行综合分析是性能分析的第一步,通过分析测试时间 ...

  8. 转:asp.net mvc ef 性能监控调试工具 MiniProfiler

    MiniProfiler官网:http://miniprofiler.com/ MiniProfiler的一个特别有用的功能是它与数据库框架的集成.除了.NET原生的 DbConnection类,Mi ...

  9. 【转】JVM虚拟性能分析

    JDK自带的JAVA性能分析工具.它已经在你的JDK bin目录里了,只要你使用的是JDK1.6 Update7之后的版本.点击一下jvisualvm.exe图标它就可以运行了. 这里是VisualV ...

随机推荐

  1. python之pymongo

    引入 在这里我们来看一下Python3下MongoDB的存储操作,在本节开始之前请确保你已经安装好了MongoDB并启动了其服务,另外安装好了Python的PyMongo库. MongoDB 数据库安 ...

  2. 使用Spring Boot Actuator将指标导出到InfluxDB和Prometheus

    使用Spring Boot Actuator将指标导出到InfluxDB和Prometheus   Spring Boot Actuator是Spring Boot 2发布后修改最多的项目之一.它经过 ...

  3. vue实战记录(二)- vue实现购物车功能之创建vue实例

    vue实战,一步步实现vue购物车功能的过程记录,课程与素材来自慕课网,自己搭建了express本地服务器来请求数据 作者:狐狸家的鱼 本文链接:vue实战-实现购物车功能(二) GitHub:sue ...

  4. 【洛谷P1313 计算系数】

    题目连接 #include<algorithm> #include<iostream> #include<cstring> #include<cstdio&g ...

  5. TypeError: 'NoneType' object is not subscriptable

    运行,显示TypeError: 'NoneType' object is not subscriptable错误信息,原因是变量使用了系统内置的关键字list 重新定义下这个变量就好了

  6. Dubbo学习笔记8:Dubbo的线程模型与线程池策略

    Dubbo默认的底层网络通讯使用的是Netty,服务提供方NettyServer使用两级线程池,其中 EventLoopGroup(boss) 主要用来接受客户端的链接请求,并把接受的请求分发给 Ev ...

  7. 第十节:数据批注(DataAnnotationModel)和自定义验证(包括Model级别的验证)

    一. 简介 写完上一个章节MVC中的常用特性,迫不及待将该系列补全,该章节主要介绍数据批注(也叫:注解). 一听[数据批注],好高大上的名字,但仔细一看,它们其实是[System.ComponentM ...

  8. [物理学与PDEs]第1章第8节 静电场和静磁场 8.2 稳定电流的电场

    1. 此时, Maxwell 方程组为 $$\beex \bea \Div{\bf D}&=\rho_f,\\ \rot {\bf E}&={\bf 0},\\ \Div{\bf B} ...

  9. DataBase vs Data Warehouse

    Database https://en.wikipedia.org/wiki/Database A database is an organized collection of data.[1] A ...

  10. Matplotlib画图详解

    from matplotlib import pyplot as plt #调节图形大小,宽,高 plt.figure(figsize=(6,9)) #定义饼状图的标签,标签是列表 labels = ...