文章出现的原因
很久没写关于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 程序效果截图