刚才在博客园看了篇文章,http://www.cnblogs.com/cmt/p/csharp_regex_timeout.html  突然联想到以前遇到的问题,w3wp进程吃光CPU都挂起IIS进程,即使网站或者服务出现了问题,导致CPU百分之百,但是有时候我们根本不知道具体哪里出了问题!公司曾经一个项目组遇到过WCF服务端莫名其妙就CPU占用百分之百的情况,但是具体有查不到原因。我总结了下,查不到原因是因为没有合理的方法,根本不知道从何开始查。如果当时对客户端或者服务端方法调用都加上日志,估计很快就查到了是哪个方法出了问题。

如果在在MVC或者WEBAPI中,记录每个action的执行时间,后期用来做一些分析,我们会发现哪些调用是执行耗时是比较久的,下面分享下我们在项目中是如何做的。

记录每个action的执行时间,这个需要点技巧。记录下层调用的时间也需要一些技巧的,最开始我想用动态代理来实现,后来发现这个东西不是那么的友好,后来换了种思路去实现。

首先介绍MVC和WEBAPI如何记录action的执行时间

MVC的实现

using System.Diagnostics;
using System.Globalization;
using System.Web.Mvc; namespace WebSeat.Web.Core
{
public class TimingActionFilter : IActionFilter
{
private const string Key = "__action_duration__"; public void OnActionExecuting(ActionExecutingContext filterContext)
{
var stopWatch = new Stopwatch();
stopWatch.Start();
//filterContext.HttpContext.Request.Params[Key] = stopWatch;
filterContext.HttpContext.Items[Key] = stopWatch;
} public void OnActionExecuted(ActionExecutedContext filterContext)
{
var items = filterContext.HttpContext.Items;
if (!items.Contains(Key))
{
return;
}
var stopWatch = items[Key] as Stopwatch;
if (stopWatch == null)
{
return;
}
stopWatch.Stop();
var actionName = filterContext.ActionDescriptor.ActionName;
var controllerName = filterContext.ActionDescriptor.ControllerDescriptor.ControllerName;
#if DEBUG
Debug.WriteLine("Execute {0}- {1} took {2}.]", controllerName, actionName, stopWatch.ElapsedMilliseconds);
#endif var response = filterContext.HttpContext.Response;
if (response != null)
{
response.AddHeader("TotalSpendTimes",
stopWatch.ElapsedMilliseconds.ToString(CultureInfo.InvariantCulture));
}
}
}
}

最后在Application_Start中注册一下

 FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);

public class FilterConfig
{
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new HandleErrorAttribute());
filters.Add(new TimingActionFilter());
}
}

WEBAPI中的实现

using System.Diagnostics;
using System.Globalization;
using System.Web.Http.Controllers;
using System.Web.Http.Filters; namespace WebSeat.FlipCourse.WebApi.Resolver.Filter
{
public class TimingActionFilter : ActionFilterAttribute
{
private const string Key = "__action_duration__";
public override void OnActionExecuting(HttpActionContext actionContext)
{
var stopWatch = new Stopwatch();
actionContext.Request.Properties[Key] = stopWatch;
stopWatch.Start();
} public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
{
if (!actionExecutedContext.Request.Properties.ContainsKey(Key))
{
return;
}
var stopWatch = actionExecutedContext.Request.Properties[Key] as Stopwatch;
if (stopWatch == null)
{
return;
}
stopWatch.Stop();
var actionDescriptor = actionExecutedContext.ActionContext.ActionDescriptor;
var actionName = actionDescriptor.ActionName;
var controllerName = actionDescriptor.ControllerDescriptor.ControllerName;
#if DEBUG
Debug.WriteLine("Execute {0}- {1} took {2}.]", controllerName, actionName, stopWatch.ElapsedMilliseconds);
//可直接记录到日志中
#endif
if (actionExecutedContext.Response != null)
{
actionExecutedContext.Response.Content.Headers.Add("TotalSpendTimes", stopWatch.ElapsedMilliseconds.ToString(CultureInfo.InvariantCulture));
}
}
}
}

上面记录Controller里面action的执行时间,action其实调用了底层方法,其实也就记录了整个调用过程的耗时。但是如果我们的程序没有被MVC或者WEBAPI呢?比如我们只是用WCF来提供服务,此时没有了Filter,这就麻烦了。但由于我们项目一直采用分层架构,上层肯定会调用下层,下层返回相应的数据,并且返回相关的调用状态。

底层我们统一返回InvokeResult<T>泛型对象,里面包含返回的数据以及此次操作的其他信息。

  /// <summary>
/// 操作状结果处理--有返回值
/// </summary>
/// <typeparam name="T"></typeparam>
public class HandleResult<T>
{
public HandleResult()
{
Success = true;
InvokCode = InvokeCode.Success;
} public T Result { get; set; }
public string Message{ get; set; }
public InvokeCode InvokCode { get; set; }
public bool Success { get; set; }
}

上层调用下层并向客户端返回ClientResult<T>泛型对象

/// <summary>
/// 操作状结果处理--有返回值
/// </summary>
/// <typeparam name="T"></typeparam>
public class ClientResult<T>
{ public T Result { get; set; } public string Message { get; set; } public bool Success { get; set; }
}

为什么要统一返回一种类型呢,这个我不多说,用过的人自然之道他的好处。现在就要做一个转换,把下层返回的HandleResult<T>转换为ClientResult<T>。如果在项目中只用HandleResult<T>进行传输,那么就不需要转换。我们的项目需要转换也是有特殊的需求。不管转不转换,反正就是调用下层,下层返回一个HandleResult<T>对象,抓住这个特点,我们可以做很多事情。我们可以定义一个类,类里面Invoke有这个方法

//不需要转换的操作

     /// <summary>
/// 调用方法 返回ClientResult<TResult/> 但是没有对Result进行映射转换
/// </summary>
/// <typeparam name="TResult"></typeparam>
/// <param name="fun"></param>
/// <returns></returns>
public static ClientResult<TResult> Invoke<TResult>(Func<HandleResult<TResult>> fun)
{
var result = new ClientResult<TResult> { Success = true };
var stopWatch = new Stopwatch();
stopWatch.Start();
try
{
HandleResult<TResult> data = fun();
result.Success = data.Success;
result.Message = data.Message;
result.Result = data.Result;
}
catch (WebSeatException ex)
{
result.Success = false;
result.Message = string.Format("{0}({1})", ex.Code.ToMessage(), ex.Description);
}
catch (Exception ex)
{
result.Success = false;
result.Message = ex.GetMessage();
LogHelper.Error(ex);
}
finally
{
stopWatch.Stop();
var time = stopWatch.ElapsedMilliseconds;
if (time > )
{
#if DEBUG
LogHelper.Error(new Exception(),
string.Format("调用方法:{0}.{1}, 执行时间:{2}毫秒", fun.Method.DeclaringType, fun.Method.Name, time));
#endif
}
else
{
LogHelper.Log(string.Format("调用方法:{0}.{1}, 执行时间:{2}毫秒", fun.Method.DeclaringType, fun.Method.Name, time));
}
}
return result;
}

//需要转换的  如果有需要用Mapper做数据映射,可以给这个方法加个重载,代码如下,具体实现和上面差不多。

 /// <summary>
/// 调用方法读取数据 并且 把 HandleResult<TGResult/>转换成 ClientResult<TResult/>
/// </summary>
/// <typeparam name="TGResult"></typeparam>
/// <typeparam name="TResult"></typeparam>
/// <param name="fun"></param>
/// <returns></returns>
public static ClientResult<TResult> Invoke<TGResult, TResult>(Func<HandleResult<TGResult>> fun)
{
var result = new ClientResult<TResult> { Success = true };
var stopWatch = new Stopwatch();
stopWatch.Start();
try
{
HandleResult<TGResult> data = fun();
result.Success = data.Success;
result.Message = data.Message;
result.Result = Mapper.Map<TGResult, TResult>(data.Result);
}
catch (WebSeatException ex)
{
result.Success = false;
result.Message = string.Format("{0}({1})", ex.Code.ToMessage(), ex.Description);
}
catch (Exception ex)
{
result.Success = false;
result.Message = ex.GetMessage();
LogHelper.Error(ex);
}
finally
{
stopWatch.Stop();
var time = stopWatch.ElapsedMilliseconds;
if (time > )
{
#if DEBUG
LogHelper.Error(new Exception(),
string.Format("调用方法:{0}.{1}, 执行时间:{2}毫秒", fun.Method.DeclaringType, fun.Method.Name, time));
#endif
}
else
{
LogHelper.Log(string.Format("调用方法:{0}.{1}, 执行时间:{2}毫秒", fun.Method.DeclaringType, fun.Method.Name, time));
}
}
return result;
}

我们可以看到,在上面的方法中,我们对调用过程做了包装,并做了一些转换,上层调用下层我们规定必须使用Invoke这个方法,相当于他是上层调用下层唯一的通道,所有的调用都要从此进过,这样设计我们就能够对所有的调用相关的记录。

上层调用下层的分几种情况,如果是WCF做服务端,使用和以下一样,我这里还是用的Controller里面的action做示例,示例代码如下:

     
    public JsonResult GetGradeList()
{
return Json(WebExtension.Invoke(() => GradeService.GetGradeList()));
} public JsonResult GetGradeBySchoolIdAndSchoolStageId(int schoolId = , int schoolStageId = )
{
return Json(WebExtension.Invoke(() => GradeService.GetGradeBySchoolIdAndSchoolStageId(schoolId, schoolStageId)));
}      public JsonResult GetAccountById(int id = )
{
return Json(WebExtension.Invoke<AccountDTO, AccountModel>(() => AccountDetailService.GetAccountById(id)), JsonRequestBehavior.AllowGet);
} public JsonResult GetOneSchool()
{
return Json(WebExtension.Invoke<SchoolDTO, SchoolViewModel>(() => SchoolService.GetOneSchool()));
}

记录的日志如下,一般的调试信息记录在debug中,错误信息记录在error中,如果单个方法调用超过3秒直接记录到错误日志中。

在MVC或WEBAPI中记录每个Action的执行时间和记录下层方法调用时间的更多相关文章

  1. MVC与WebApi中的异常过滤器

    一.MVC的异常过滤器   1.自定义MVC异常过滤器 创建一个类,继承HandleErrorAttribute即可,如果不需要作为特性使用直接实现IExceptionFilter接口即可, 注意,该 ...

  2. 关于mvc、webapi中get、post、put、delete的参数

    webapi中post提交的数据必须是一个类,get提交的数量无所谓 多种参数get时,参数名不能相同: 在能通过c#的校验的前提下,参数名.参数数量不能全完相同 public string Get( ...

  3. MVC路由规则以及前后台获取Action、Controller、ID名方法

    1.前后台获取Action.Controller.ID名方法 前台页面:ViewContext.RouteData.Values["Action"].ToString(); Vie ...

  4. 【C#小知识】C#中一些易混淆概念总结---------数据类型存储,方法调用,out和ref参数的使用

    这几天一直在复习C#基础知识,过程中也发现了自己以前理解不清楚和混淆的概念.现在给大家分享出来我的笔记: 一,.NET平台的重要组成部分都是有哪些 1)FCL (所谓的.NET框架类库) 这些类是微软 ...

  5. WebApi官网学习记录---webapi中controller与action的选择

    如果framework找到一个匹配的URI,创建一个包含占位符值的字典,key就是这些占位符(不包括大括号),value来自URI或者默认值,这个字典存储在IHttpRouteData对象中.默认值可 ...

  6. MVC与WebApi中的异常统一处理

    1.简单例子 /// <summary> /// 全局页面控制器异常记录 MVC的异常处理 /// </summary> public class CustomErrorAtt ...

  7. MVC和WebApi中设置Area中的页为首页

    拿WebApi为例,我们一般会生成一份帮助文档,帮助文档会在Area中 我们现在要讲帮助文档设为首页 只需在App_Start文件夹下添加 RouteConfig 类 public class Rou ...

  8. C#.Net Mvc运营监控,计算方法/接口/action/页面执行时间

    1.建立一个TimingActionFilter过滤器 public class TimingActionFilter : ActionFilterAttribute { public overrid ...

  9. ASP.NET MVC 在控制器中接收视图表单POST过来的数据方法

    方法一:通过Request.Form [HttpPost]        public ActionResult Test()        {            string id=Reques ...

随机推荐

  1. MVC1笔记

    /// ///直接返回 字符串的 Action方法,适用于 不需要返回大量 html代码的业务(类似于一般处理程序) /// public string Index() { return " ...

  2. DELETE---删除记录

    DELETE FROM table_name [WHERE conditions]; 例: DELETE FROM userinfo WHERE name='zhangsan'; 说明 1.不添加条件 ...

  3. mysql数据库时间、字符串类型互转

    时间格式转换: select DATE_FORMAT(NOW(),"%y-%m-%d %H:%i:%s") 字符串转时间: select STR_TO_DATE("201 ...

  4. C# Quartz.Net 定时任务的简单使用

    最近做了一个定时执行任务的软件. 执行任务时,会使用log4net记录日志,如果执行任务有异常,则发送邮件给指定的对象. 我做的是每天的9点和16点执行一次任务,以此记录: 首先,获得Quartz.N ...

  5. 使用POI操作Excel使用小总结

    1. Workbook维护一个调色板,可以自定义设置56种颜色,下标从8到63. 用到颜色的地方,可以输入下标获取颜色,如CellStyle的setFillForegroundColor(); 2.C ...

  6. 面试相关的技术问题---java基础

    最近在准备秋季校招,将一些常见的技术问题做一个总结!希望对大家有所帮助! 1.面向对象和面向过程的区别是什么? 面向对象是把构成问题的事务分解成各个对象,建立对象的目的不是为了完成一个步骤,而是为了描 ...

  7. Quora图片懒加载

    浏览quora的时候发现他的懒加载有点特别,然后就看了一下 然后等图片下载后: 就是上传图片的时候就把图片给压缩成了10X10的小图片,然后加载的时候放大价格模糊的滤镜

  8. C++:memset ,memcpy 和strcpy 的根本区别!

    #include <stdio.h> #include <stdlib.h> #include <string.h> #include <assert.h&g ...

  9. Nagios-配置版

    1  概念(简介) Nagios是插件式的结构,它本身没有任何监控功能,所有的监控都是通过插件进行的,因此其是高度模块化和富于弹性的.Nagios监控的对象可分为两类:主机和服务.主机通常指的是物理主 ...

  10. 文件(夹)比较 Beyond Compare, Diff

    文件(夹)比较 Beyond Compare, Diff, UltraCompare 1.Beyond Compare(无与伦比) 2.Diff 参考 1.diff详解