天天看點

愛上MVC3系列~監視Action的運作時間,并提供逾時記錄機制

回到目錄

文章出現的原因

很久沒寫關于MVC的文章了,原因是将關注點移向了MVVM和DDD這邊,而這篇文章完全是因為公司項目的需要,因為公司網站總是不定時的502,而這由可能是程式逾時所引起的,為了分析出現問題的點,是以,對action進行了監控,這個監控功能我選擇了在global裡注入全局的filter來實作這個功能,為了避免并發,所選擇了将記錄存儲到cache的隊列裡,再通過quartZ的任務排程功能,來實作資料的IO寫入或者資料庫與入.

系統流程圖

用代碼說話

1 建立一個Filter

/// <summary>
    /// Action渲染頁面所需要的時間
    /// </summary>
    public class ActionRenderTimeAttribute : System.Web.Mvc.ActionFilterAttribute
    {
      

        /// <summary>
        /// 鎖對象
        /// </summary>
        static object lockObj = new object();
        /// <summary>
        /// 記錄進行Action的時間
        /// </summary>
        DateTime joinTime;
        /// <summary>
        /// 進行action之前
        /// </summary>
        /// <param name="filterContext"></param>
        public override void OnActionExecuting(System.Web.Mvc.ActionExecutingContext filterContext)
        {
            joinTime = DateTime.Now;
            base.OnActionExecuting(filterContext);
        }
        /// <summary>
        /// 渲染頁面HTML之後
        /// </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加全局注入點

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

3 建立一個QuartZ的任務

/// <summary>
    /// 工作任務基類
    /// </summary>
    public abstract class JobBase
    {
        /// <summary>
        /// log4日志對象
        /// </summary>
        protected log4net.ILog Logger
        {
            get
            {
                return log4net.LogManager.GetLogger(this.GetType());//得到目前類類型(目前實執行個體化的類為具體子類)
            }
        }
    }      
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)
                        {
                            //! 逾時,開始記錄日志
                            global::Logger.Core.LoggerFactory.Instance.Logger_Info(
                                        string.Format("出現異常的頁面:{0},頁面加載需要的時間:{1}秒,異常發生時間:{2}"
                                        , temp.Item2, temp.Item1, DateTime.Now),"actionTime.log");
                        }
                    }
                }
                catch (Exception ex )
                {

                    throw ex;
                }
            }
        }

        #endregion
    }      

4 在global裡配置QuartZ注入點

#region quartZ排程中心
            const string DEFAULTINTERVAL = "300";//預設為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裡配置相關排程和性能監控的資訊

<!-- 每次得到的資料行數,以便減少記憶體的占用-->
    <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 程式效果截圖

作者:倉儲大叔,張占嶺,

榮譽:微軟MVP

QQ:853066980

支付寶掃一掃,為大叔打賞!

愛上MVC3系列~監視Action的運作時間,并提供逾時記錄機制