自製ActionFilter小鋼炮:MVC/WebAPI程式效能計時器
最近進行一些ASP.NET MVC/ASP.NET Web API系統效能改善的工作,改善之初當然是找出最耗效能(或是說較耗效能)的程式碼來下手改善。找出吃資源程式碼的方式很多,第三方有許多不錯的選擇,不過我需求真的很簡單,只想得到一個數字,在 .NET Framework 最簡單是用的System.Diagnostics.Stopwatch 類別進行耗用時間的測量。
單一程式段落效能計時器
我們先從最簡單的開始,計算一段程式碼的執行時間。
ASP.NET MVC - Action段落效能計時器
public ActionResult InActionTimer() { System.Diagnostics.Stopwatch stopWatch = new System.Diagnostics.Stopwatch(); stopWatch.Start(); // 模擬程式執行 System.Threading.Thread.Sleep(1527); stopWatch.Stop(); TimeSpan ts = stopWatch.Elapsed; System.Diagnostics.Debug.WriteLine($"ActionTimer: {ts.ToString()} from Foo/InActionTimer"); return Content($"ActionTimer: {ts.ToString()} from Foo/InActionTimer"); }
可以看到.Start()
與.Stop()
之間的程碼計算出耗費時間。
ASP.NET Web API - Action段落效能計時器
public IHttpActionResult GetInActionTimer() { System.Diagnostics.Stopwatch stopWatch = new System.Diagnostics.Stopwatch(); stopWatch.Start(); System.Threading.Thread.Sleep(1527); stopWatch.Stop(); TimeSpan ts = stopWatch.Elapsed; System.Diagnostics.Debug.WriteLine($"ActionTimer: {ts.ToString()} from Bar/GetInActionTimer"); return Ok($"ActionTimer: {ts.ToString()} from Bar/GetInActionTimer"); }
Stopwatch 的程式碼幾乎一模一樣。
雖然 Stopwatch 的程式碼非常簡單,但當我們大量使用時就很容易發現一個壞味道。以 ASP.NET MVC 為例:
public ActionResult InActionTimer1() { System.Diagnostics.Stopwatch stopWatch = new System.Diagnostics.Stopwatch(); stopWatch.Start(); // 模擬程式執行 System.Threading.Thread.Sleep(1527); stopWatch.Stop(); TimeSpan ts = stopWatch.Elapsed; System.Diagnostics.Debug.WriteLine($"ActionTimer: {ts.ToString()} from Foo/InActionTimer1"); return Content($"ActionTimer: {ts.ToString()} from Foo/InActionTimer1"); } public ActionResult InActionTimer2() { System.Diagnostics.Stopwatch stopWatch = new System.Diagnostics.Stopwatch(); stopWatch.Start(); // 模擬程式執行 System.Threading.Thread.Sleep(1527); stopWatch.Stop(); TimeSpan ts = stopWatch.Elapsed; System.Diagnostics.Debug.WriteLine($"ActionTimer: {ts.ToString()} from Foo/InActionTimer2"); return Content($"ActionTimer: {ts.ToString()} from Foo/InActionTimer2"); }
好呆哦!
小鋼炮ActionFilter上場
ActionFilter作用於MVC/Web API的Action方法之前與之後,這種計時Action方法情境最合適的不二人選當然是ActionFilter。
ASP.NET MVC - ActionTimerAttribute
public class ActionTimerAttribute : ActionFilterAttribute { private Stopwatch swAction; private Stopwatch swView; public ActionTimerAttribute() { swAction = new Stopwatch(); swView = new Stopwatch(); } #region 計算 Action 執行時間 public override void OnActionExecuting(ActionExecutingContext filterContext) { swAction.Start(); base.OnActionExecuting(filterContext); } public override void OnActionExecuted(ActionExecutedContext filterContext) { var controllerName = (string)filterContext.RouteData.Values["controller"]; var actionName = (string)filterContext.RouteData.Values["action"]; swAction.Stop(); TimeSpan ts = swAction.Elapsed; swAction.Reset(); Debug.WriteLine($"ActionTimer: {ts.ToString()} from {controllerName}/{actionName}"); base.OnActionExecuted(filterContext); } #endregion #region 計算 View 執行時間 public override void OnResultExecuting(ResultExecutingContext filterContext) { swView.Start(); base.OnResultExecuting(filterContext); } public override void OnResultExecuted(ResultExecutedContext filterContext) { var controllerName = (string)filterContext.RouteData.Values["controller"]; var actionName = (string)filterContext.RouteData.Values["action"]; swView.Stop(); TimeSpan ts = swView.Elapsed; swView.Reset(); Debug.WriteLine($"ViewTimer: {ts.ToString()} from {controllerName}/{actionName}"); base.OnResultExecuted(filterContext); } #endregion }
利用ASP.NET MVC的ActionFilterAttribute
的OnActionExecuting
、OnActionExecuted
、OnResultExecuting
、OnResultExecuted
四大天王,我們不只可以計算Action方法的耗費時間,我們連View Engine的耗費時間都能輕鬆計算出。
ASP.NET Web API - ActionTimerAttribute
public class ActionTimerAttribute : ActionFilterAttribute { private Stopwatch sw; public ActionTimerAttribute() { sw = new Stopwatch(); } #region 計算 API Action 執行時間 public override void OnActionExecuting(HttpActionContext actionContext) { sw.Start(); base.OnActionExecuting(actionContext); } public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext) { string controllerName = actionExecutedContext.ActionContext.ControllerContext.ControllerDescriptor.ControllerName; string actionName = actionExecutedContext.ActionContext.ActionDescriptor.ActionName; sw.Stop(); TimeSpan ts = sw.Elapsed; sw.Reset(); Debug.WriteLine($"ActionTimer: {ts.ToString()} from {controllerName}/{actionName}"); base.OnActionExecuted(actionExecutedContext); } #endregion }
ASP.NET Web API 的 ActionFilter 相對於 ASP.NET MVC 相對於簡單許多。
輸出至client header
耗費時間除了輸出至"輸出視窗"或"日誌系統"外,我們也能將相關資訊輸出至回應訊息的header中。
public override void OnActionExecuted(ActionExecutedContext filterContext) { var controllerName = (string)filterContext.RouteData.Values["controller"]; var actionName = (string)filterContext.RouteData.Values["action"]; swAction.Stop(); TimeSpan ts = swAction.Elapsed; swAction.Reset(); Debug.WriteLine($"ActionTimer: {ts.ToString()} from {controllerName}/{actionName}"); // 輸出至 client header var httpContext = filterContext.HttpContext; var response = httpContext.Response; response.AddHeader("X-Stopwatch", ts.ToString()); base.OnActionExecuted(filterContext); }
沒有留言:
張貼留言
感謝您的留言,如果我的文章你喜歡或對你有幫助,按個「讚」或「分享」它,我會很高興的。