愛(ai)上MVC3系列(lie)~監視Action的(de)運行(xing)時(shi)間,并提供超時(shi)記錄機制(zhi)
文章出現的原因
很(hen)久沒寫關于MVC的(de)(de)文章了(le),原因是(shi)(shi)將關注點移(yi)向了(le)MVVM和DDD這邊,而(er)這篇文章完全是(shi)(shi)因為(wei)公司項目的(de)(de)需要(yao),因為(wei)公司網(wang)站總是(shi)(shi)不定時(shi)的(de)(de)502,而(er)這由可(ke)能是(shi)(shi)程序超時(shi)所(suo)(suo)引起的(de)(de),為(wei)了(le)分析出現問(wen)題的(de)(de)點,所(suo)(suo)以,對action進行了(le)監(jian)控(kong),這個(ge)監(jian)控(kong)功能我選(xuan)擇了(le)在global里(li)注入全局的(de)(de)filter來實現這個(ge)功能,為(wei)了(le)避免并發,所(suo)(suo)選(xuan)擇了(le)將記(ji)錄存儲到cache的(de)(de)隊(dui)列(lie)里(li),再通過quartZ的(de)(de)任務調度功能,來實現數據的(de)(de)IO寫入或者數據庫與入.
系統流程圖
用代碼說話
1 建立(li)一個Filter
/// <summary> /// Action渲染(ran)頁面(mian)所(suo)需要(yao)的時(shi)間 /// </summary> public class ActionRenderTimeAttribute : System.Web.Mvc.ActionFilterAttribute { /// <summary> /// 鎖(suo)對象(xiang) /// </summary> static object lockObj = new object(); /// <summary> /// 記錄進行Action的(de)時間 /// </summary> DateTime joinTime; /// <summary> /// 進行(xing)action之(zhi)前(qian) /// </summary> /// <param name="filterContext"></param> public override void OnActionExecuting(System.Web.Mvc.ActionExecutingContext filterContext) { joinTime = DateTime.Now; base.OnActionExecuting(filterContext); } /// <summary> /// 渲染頁面HTML之(zhi)后 /// </summary> /// <param name="filterContext"></param> public override void OnResultExecuted(System.Web.Mvc.ResultExecutedContext filterContext) { int outSeconds;//! 超時的秒數,默認為60S int.TryParse((System.Configuration.ConfigurationManager.AppSettings["ActionRenderTime"] ?? "60").ToString(), out outSeconds); var timeSpan = (DateTime.Now - joinTime).Seconds; if (timeSpan > outSeconds) { lock (lockObj) { var temp = (System.Web.HttpRuntime.Cache["RunTime"] as Queue<Tuple<int, string>>) ?? new Queue<Tuple<int, string>>(); temp.Enqueue(new Tuple<int, string>(timeSpan, filterContext.RequestContext.HttpContext.Request.Url.AbsoluteUri)); System.Web.HttpRuntime.Cache.Insert("RunTime", temp); } } base.OnResultExecuted(filterContext); } }
2 為filter加全局(ju)注入點
public class FilterConfig { public static void RegisterGlobalFilters(GlobalFilterCollection filters) { filters.Add(new HandleErrorAttribute()); filters.Add(new MVVM.ActionRenderTimeAttribute()); } }
3 建(jian)立一個QuartZ的(de)任務
/// <summary> /// 工作任務基類 /// </summary> public abstract class JobBase { /// <summary> /// log4日志對象 /// </summary> protected log4net.ILog Logger { get { return log4net.LogManager.GetLogger(this.GetType());//得到當(dang)前類類型(當(dang)前實(shi)實(shi)例化的類為具體(ti)子類) } } }
public class ActionTimeJob : JobBase, IJob { #region Fields & Properties /// <summary> /// 鎖對象 /// </summary> private static object lockObj = new object(); #endregion #region IJob 成員 public void Execute(IJobExecutionContext context) { lock (lockObj) { try { if ((System.Web.HttpRuntime.Cache["RunTime"] as Queue<Tuple<int, string>>) != null && (System.Web.HttpRuntime.Cache["RunTime"] as Queue<Tuple<int, string>>).Count > 0) { var temp = (System.Web.HttpRuntime.Cache["RunTime"] as Queue<Tuple<int, string>>).Dequeue(); if (temp != null) { //! 超時,開始記錄日(ri)志 global::Logger.Core.LoggerFactory.Instance.Logger_Info( string.Format("出現異常(chang)的頁面:{0},頁面加載需要的時間:{1}秒,異常(chang)發生時間:{2}" , temp.Item2, temp.Item1, DateTime.Now),"actionTime.log"); } } } catch (Exception ex ) { throw ex; } } } #endregion }
4 在(zai)global里配置QuartZ注入(ru)點
#region quartZ調度中心 const string DEFAULTINTERVAL = "300";//默認為(wei)5分鐘 string user_Classroom_RJobInterval = ConfigurationManager.AppSettings["ActionRunTimeJob"] ?? DEFAULTINTERVAL; ISchedulerFactory sf = new Quartz.Impl.StdSchedulerFactory(); IScheduler sched = sf.GetScheduler(); //一個工作可以由多個組組成,而每個組又可以由多個trigger組成 IDictionary<IJobDetail, IList<ITrigger>> scheduleJobs = new Dictionary<IJobDetail, IList<ITrigger>>(); #region ActionRunTimeJob scheduleJobs.Add(JobBuilder.Create<ActionTimeJob>() .WithIdentity("job1", "group1") .Build(), new List<ITrigger> { (ICronTrigger)TriggerBuilder.Create() .WithIdentity("trigger", "group1") .WithCronSchedule(user_Classroom_RJobInterval) .Build() }); sched.ScheduleJobs(scheduleJobs, true); sched.Start(); #endregion #endregion
5 在config里配置相關調度和性(xing)能(neng)監控的信息
<!-- 每次得到的數據行數,以便減少內存的占用-->
<add key="DataRow" value="1"/>
<!-- 每3秒執行一次-->
<add key="ActionRunTimeJob" value="0/3 * * * * ?"/>
<!-- 每頁面渲染時間超時為1秒-->
<add key="ActionRenderTime" value="1"/>
<!-- 是否開啟action性能監控-->
<add key="isActionRender" value="1"/>
6 程序效果截圖