关于Quartz.NET作业调度框架的一点小小的封装,实现伪AOP写LOG功能

本文涉及的产品
日志服务 SLS,月写入数据量 50GB 1个月
简介:

Quartz.NET是一个非常强大的作业调度框架,适用于各种定时执行的业务处理等,类似于WINDOWS自带的任务计划程序,其中运用Cron表达式来实现各种定时触发条件是我认为最为惊喜的地方。

Quartz.NET主要用到下面几个类:

IScheduler --调度器

IJobDetail --作业任务

ITrigger --触发器

如果我们自己采用Timer来写类似的定时执行任务程序的话,相应的我们应该有:(以下均为设想,目的是让大家搞清楚Quartz.NET上面三个接口的关系)

ScheduleTimer --Timer,每秒执行一次;

TriggerClass --判断是否需要执行作业任务,ScheduleTimer 每执行一次,就应该新开线程调用TriggerClass成员 NeedExecute方法或属性;

JobClass--具体的作业任务类,TriggerClass,若TriggerClass.NeedExecute返回true,那么就应该执行JobClass成员Execute方法;

好了,有关Quartz.NET的介绍非常之多,我这里不在多说,下面将主要介绍如何实现伪AOP写LOG功能。

AOP不知道,请点击此处了解。

Quartz.NET虽然已经集成了log4net的写日志功能,只需在Config配置好即可,但我觉得框架里面写的日志不符合我的要求,故我需要按照实际业务需要在某些条件才进行写LOG,故才有了这篇文章。

以下是实现了一个Job包裹类,也可以看作是Job的代理类,完整代码如下:

    [DisallowConcurrentExecution]
    public class JobWraper<TJob> : IJob where TJob : IJob, new()
    {
        private static int syncFlag = 0;
        private IJob jobInner = null;

        public JobWraper()
        {
            jobInner = Activator.CreateInstance<TJob>();
        }

        public void Execute(IJobExecutionContext context)
        {
            if (Interlocked.Increment(ref syncFlag) != 1) return; //忙判断
            try
            {
                jobInner.Execute(context);
            }
            catch (Exception ex)
            {
                Master.WriteMsg(context.JobDetail.Key + "执行异常:" + ex.Message + Environment.NewLine + ex.StackTrace, true, true);
            }

            Interlocked.Exchange(ref syncFlag, 0); //解除忙
        }

代码很简单,一般人都看得懂,我只是说重点:

1.syncFlag静态字段,目的是用来标记是否忙或者不忙,1代表不忙,其它代表忙,Interlocked.Increment与Interlocked.Exchange的用法是原子级的,确保每次只能有一个线程进行操作,类似于SQL中的独占锁,与lock有点相同,但又不同,如果用lock将整个执行都用大括号包起来,那么锁的范围比较广而且不易控制,而Interlocked只需要在需要的时候才独占,而且独占的时间非常短,其他大部份时间都是正常,而且更易可控,这就是我喜欢用他的原因。

2.为什么标记忙与不忙,原因是我必需确保每次执行的业务逻辑能够执行完成,而不要出现未执行完成,下一次的执行点又到了,造成多次甚至重复执行。

2.为什么要包裹,原因是我不想每个Job类里面都写try catch异常捕获及忙与不忙的判断,这样普通类只需专注业务处理即可。至于被包裹的类不一定非要IJob接口,可以自定义各类接口,但一定要有无参构造函数,否则就无法创建包裹的类的实例了。

通过上面的讲解,大家应该都明白了,下面是我为了便于集成管理Job,封装了一个JobManager任务管理类,完整代码如下:(代码比较简单,不再说明)

    public class JobManager
    {

        private IScheduler scheduler = null;
        private int schedulerState = 0;


        public Dictionary<string, JobWithTrigger> JobTriggers
        {
            get;
            private set;
        }

        private IScheduler GetAScheduler()
        {
            var stdSchedulerFactory = new StdSchedulerFactory();
            scheduler = stdSchedulerFactory.GetScheduler();
            return scheduler;
        }

        public JobManager()
        {
            scheduler = GetAScheduler();
            JobTriggers = new Dictionary<string, JobWithTrigger>();
        }

        public JobWithTrigger CreateJobWithTrigger<TJob>(string cronExpr, IDictionary<string, object> jobData = null) where TJob : IJob
        {
            var jobType = typeof(TJob);
            string jobTypeName = jobType.Name;
            if (jobType.IsGenericType)
            {
                jobTypeName = jobType.GetGenericArguments()[0].Name;
            }

            IJobDetail job = null;

            if (jobData == null)
                job = JobBuilder.Create<TJob>().WithIdentity(jobTypeName).Build();
            else
                job = JobBuilder.Create<TJob>().WithIdentity(jobTypeName).UsingJobData(new JobDataMap(jobData)).Build();

            ITrigger trigger = TriggerBuilder.Create().WithIdentity(jobTypeName + "-Trigger").ForJob(job).StartNow().WithCronSchedule(cronExpr).Build();

            var jt = new JobWithTrigger(job, trigger);
            JobTriggers[jt.Key] = jt;

            return jt;
        }

        public void ScheduleJobs(params JobWithTrigger[] jts)
        {
            if (scheduler.IsShutdown)
            {
                scheduler = GetAScheduler();
            }

            foreach (var jt in jts)
            {
                scheduler.ScheduleJob(jt.JobDetail, jt.Trigger);
            }
        }


        public void ScheduleJobs(params string[] jtKeys)
        {
            var jts = JobTriggers.Where(t => jtKeys.Contains(t.Key)).Select(t => t.Value).ToArray();
            ScheduleJobs(jts);
        }

        public void UnscheduleJobs(params TriggerKey[] triggerKeys)
        {
            scheduler.UnscheduleJobs(triggerKeys.ToList());
        }

        public void UnscheduleJobs(params string[] jtKeys)
        {
            var triggerKeyObjs = JobTriggers.Where(t => jtKeys.Contains(t.Key)).Select(t => t.Value.Trigger.Key).ToArray();
            UnscheduleJobs(triggerKeyObjs);
        }

        public int State
        {
            get
            {
                return schedulerState;  //0:未开始,1:开始,2:暂停,3:恢复,-1:停止
            }
        }


        [MethodImpl(MethodImplOptions.Synchronized)]
        public void Start()
        {
            if (schedulerState > 0) return;
            scheduler.Start();
            schedulerState = 1;
            Master.WriteMsg("AutoTimingExecSystem程序已启动,所有任务按计划开始执行。", false, true);
        }


        [MethodImpl(MethodImplOptions.Synchronized)]
        public void Stop()
        {
            if (schedulerState <= 0) return;
            scheduler.Clear();
            scheduler.Shutdown();
            schedulerState = -1;
            Master.WriteMsg("AutoTimingExecSystem程序已停止,所有任务停止执行。", false, true);
        }


        [MethodImpl(MethodImplOptions.Synchronized)]
        public void Pause()
        {
            if (schedulerState != 1) return;
            scheduler.PauseAll();
            schedulerState = 2;
            Master.WriteMsg("所有任务被取消或暂停执行。", false, true);
        }


        [MethodImpl(MethodImplOptions.Synchronized)]
        public void Resume()
        {
            if (schedulerState != 2) return;
            scheduler.ResumeAll();
            schedulerState = 1;
            Master.WriteMsg("所有任务重新恢复执行。", false, true);
        }

    }

JobWithTrigger:任务与触发器关联类

    [Serializable]
    public class JobWithTrigger
    {
        public JobWithTrigger()
        {
            this.Key = Guid.NewGuid().ToString("N");
        }

        public JobWithTrigger(IJobDetail job, ITrigger trigger)
            : this()
        {
            this.JobDetail = job;
            this.Trigger = trigger;
        }

        public IJobDetail JobDetail
        { get; set; }

        public ITrigger Trigger
        { get; set; }

        public string JobName
        {
            get
            {
                return this.JobDetail.Key.Name;
            }
        }

        public string TriggerName
        {
            get
            {
                return this.Trigger.Key.Name;
            }
        }

        public string Key
        {
            get;
            private set;
        }
    }

用法比较简单,示例代码如下:

var jobManager = new JobManager();
var jt=jobManager.CreateJobWithTrigger<JobWraper<TestJob>>("0/5 * * * * ?");

//这里面可以将jt的保存或显示到任务界面上...

jobManager.ScheduleJobs(JobWithTrigger的KEY数组 或 JobWithTrigger对象)

jobManager.Start(); 

jobManager.Stop();

jobManager支持反复开启与关闭。

本文转自 梦在旅途 博客园博客,原文链接:http://www.cnblogs.com/zuowj/p/6013040.html  ,如需转载请自行联系原作者

相关实践学习
日志服务之使用Nginx模式采集日志
本文介绍如何通过日志服务控制台创建Nginx模式的Logtail配置快速采集Nginx日志并进行多维度分析。
相关文章
|
14天前
|
消息中间件 开发框架 监控
NET任务调度框架Hangfire使用指南
Hangfire 是一个用于 .NET 应用程序的开源任务调度框架,支持长时间运行任务、定时任务等。通过简单的安装配置,即可将任务从主线程分离,提升应用性能。支持多种数据库,提供丰富的任务类型如立即执行、延迟执行和周期性任务,并有可视化管理界面 Hangfire Dashboard。还支持安全性配置及扩展插件,如 Hangfire.HttpJob,适合各种复杂场景下的任务调度需求。
41 1
NET任务调度框架Hangfire使用指南
|
18小时前
|
JSON 数据格式
.net HTTP请求类封装
`HttpRequestHelper` 是一个用于简化 HTTP 请求的辅助类,支持发送 GET 和 POST 请求。它使用 `HttpClient` 发起请求,并通过 `Newtonsoft.Json` 处理 JSON 数据。示例展示了如何使用该类发送请求并处理响应。注意事项包括:简单的错误处理、需安装 `Newtonsoft.Json` 依赖,以及建议重用 `HttpClient` 实例以优化性能。
36 2
|
19小时前
|
监控 安全 Linux
启用Linux防火墙日志记录和分析功能
为iptables启用日志记录对于监控进出流量至关重要
|
1月前
|
开发框架 安全 .NET
在数字化时代,.NET 技术凭借跨平台兼容性、丰富的开发工具和框架、高效的性能及强大的安全稳定性,成为软件开发的重要支柱
在数字化时代,.NET 技术凭借跨平台兼容性、丰富的开发工具和框架、高效的性能及强大的安全稳定性,成为软件开发的重要支柱。它不仅加速了应用开发进程,提升了开发质量和可靠性,还促进了创新和业务发展,培养了专业人才和技术社区,为软件开发和数字化转型做出了重要贡献。
26 5
|
1月前
|
传感器 人工智能 供应链
.NET开发技术在数字化时代的创新作用,从高效的开发环境、强大的性能表现、丰富的库和框架资源等方面揭示了其关键优势。
本文深入探讨了.NET开发技术在数字化时代的创新作用,从高效的开发环境、强大的性能表现、丰富的库和框架资源等方面揭示了其关键优势。通过企业级应用、Web应用及移动应用的创新案例,展示了.NET在各领域的广泛应用和巨大潜力。展望未来,.NET将与新兴技术深度融合,拓展跨平台开发,推动云原生应用发展,持续创新。
33 4
|
1月前
|
开发框架 .NET C#
.NET 技术凭借高效开发环境、强大框架支持及跨平台特性,在软件开发中占据重要地位
.NET 技术凭借高效开发环境、强大框架支持及跨平台特性,在软件开发中占据重要地位。从企业应用到电子商务,再到移动开发,.NET 均展现出卓越性能,助力开发者提升效率与项目质量,推动行业持续发展。
29 4
|
1月前
|
XML 安全 Java
【日志框架整合】Slf4j、Log4j、Log4j2、Logback配置模板
本文介绍了Java日志框架的基本概念和使用方法,重点讨论了SLF4J、Log4j、Logback和Log4j2之间的关系及其性能对比。SLF4J作为一个日志抽象层,允许开发者使用统一的日志接口,而Log4j、Logback和Log4j2则是具体的日志实现框架。Log4j2在性能上优于Logback,推荐在新项目中使用。文章还详细说明了如何在Spring Boot项目中配置Log4j2和Logback,以及如何使用Lombok简化日志记录。最后,提供了一些日志配置的最佳实践,包括滚动日志、统一日志格式和提高日志性能的方法。
317 30
【日志框架整合】Slf4j、Log4j、Log4j2、Logback配置模板
|
12天前
|
监控 安全 Apache
什么是Apache日志?为什么Apache日志分析很重要?
Apache是全球广泛使用的Web服务器软件,支持超过30%的活跃网站。它通过接收和处理HTTP请求,与后端服务器通信,返回响应并记录日志,确保网页请求的快速准确处理。Apache日志分为访问日志和错误日志,对提升用户体验、保障安全及优化性能至关重要。EventLog Analyzer等工具可有效管理和分析这些日志,增强Web服务的安全性和可靠性。
|
2月前
|
XML JSON Java
Logback 与 log4j2 性能对比:谁才是日志框架的性能王者?
【10月更文挑战第5天】在Java开发中,日志框架是不可或缺的工具,它们帮助我们记录系统运行时的信息、警告和错误,对于开发人员来说至关重要。在众多日志框架中,Logback和log4j2以其卓越的性能和丰富的功能脱颖而出,成为开发者们的首选。本文将深入探讨Logback与log4j2在性能方面的对比,通过详细的分析和实例,帮助大家理解两者之间的性能差异,以便在实际项目中做出更明智的选择。
325 3
|
22天前
|
存储 监控 安全
什么是事件日志管理系统?事件日志管理系统有哪些用处?
事件日志管理系统是IT安全的重要工具,用于集中收集、分析和解释来自组织IT基础设施各组件的事件日志,如防火墙、路由器、交换机等,帮助提升网络安全、实现主动威胁检测和促进合规性。系统支持多种日志类型,包括Windows事件日志、Syslog日志和应用程序日志,通过实时监测、告警及可视化分析,为企业提供强大的安全保障。然而,实施过程中也面临数据量大、日志管理和分析复杂等挑战。EventLog Analyzer作为一款高效工具,不仅提供实时监测与告警、可视化分析和报告功能,还支持多种合规性报告,帮助企业克服挑战,提升网络安全水平。