系统架构~高并发日志系统设计
对于一个项目来说,日志是必须的,一般日志的持久化方式有文件和数据库,而在多数情况下,我们都采用文件系统来实现,而对于高并发的情况下,频繁进行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的入口
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()); } }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
2013-08-19 陷阱~EF中的Update与Insert共用一个数据上下文
2011-08-19 MVC如何将用户控件(分部视图,RenderPartial,ViewUserControl)内容转换为字符串并输出
2011-08-19 MVC为Html对象建立一个扩展方法,使用自己的控件就像使用TextBox一样方便
2011-08-19 .NET平台把其它域名下的数据表内容列出来,比如新闻表(两个网站不在同一服务器)
2011-08-19 当数据库中字段设计为smalint或者tinyint后,程序中要求字段为枚举型,应该怎么设置