對於一個項目來講,日誌是必須的,通常日誌的持久化方式有文件和數據庫,而在多數狀況下,咱們都採用文件系統來實現,而對於高併發的狀況下,頻繁進行I/O操做,對系統的性能確定是有影響的,這個毋庸置疑!針對這種高併發的場合,咱們採用一種緩存隊列的方式來處理這個Case是比較明智的,本文主要是向各位展示一下,我所設計的《高併發日誌系統設計》,如在功能上有什麼須要改進的地方,歡迎各位來回復。數據庫
一 項目結構圖緩存
二 項目實現代碼併發
/// <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 }
從上面的代碼中,咱們能夠看到,這是使用quartz組件實現的,對某個方法進行輪訓調用的,下面是quartz的入口ide
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
而什麼時候向隊列裏添加信息這個功能尚未說,事實上,在MVC3裏有這樣一個功能,它能夠向全部action上添加一些特性(過濾器,attribute),咱們稱它爲全局過濾器,它的入口也是在global.asax裏,下面添加了一個過濾器,實現的功能是當頁面加載時間過長時,進行緩存隊列的添加,這裏默認是6秒時高併發
/// <summary> /// Action渲染頁面所須要的時間 /// </summary> public class ActionRenderTimeAttribute : System.Web.Mvc.ActionFilterAttribute { #region 本對象的timer很差使用 /// <summary> /// 存儲併發的隊列 /// </summary> public volatile static Queue<Tuple<int, string>> TempList = new Queue<Tuple<int, string>>(); /// <summary> /// 時間戳 /// </summary> static System.Timers.Timer sysTimer = new System.Timers.Timer(1000); /// <summary> /// 靜態構造 /// </summary> static ActionRenderTimeAttribute() { sysTimer.AutoReset = true; sysTimer.Enabled = true; sysTimer.Elapsed += sysTimer_Elapsed; sysTimer.Start(); } /// <summary> /// 觸發事件 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> static void sysTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e) { if (TempList.Count > 0) { lock (lockObj) { var temp = TempList.Dequeue(); //! 超時,開始記錄日誌 Logger.Core.LoggerFactory.Instance.Logger_Info( string.Format("出現異常的頁面:{0},超時時間{1}:秒,異常發生時間:{2}" , temp.Item2, temp.Item1, DateTime.Now)); } } } #endregion /// <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;//! 超時的秒數,默認爲60000ms int isActionRender;//開關 int.TryParse((System.Configuration.ConfigurationManager.AppSettings["ActionRenderTime"] ?? "6000").ToString(), out outSeconds); int.TryParse((System.Configuration.ConfigurationManager.AppSettings["isActionRender"] ?? "0").ToString(), out isActionRender); var timeSpan = (DateTime.Now - joinTime).Milliseconds; if (timeSpan > outSeconds && isActionRender == 1) { 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); } }
下面是FilterConfig注入的代碼性能
public class FilterConfig { public static void RegisterGlobalFilters(GlobalFilterCollection filters) { filters.Add(new HandleErrorAttribute()); filters.Add(new MVVM.ActionRenderTimeAttribute()); } }