回到目錄
文章出現的原因
很久沒寫關于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
支付寶掃一掃,為大叔打賞!
