程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> C# >> C#入門知識 >> 關於Quartz.NET作業調度框架的一點小小的封裝,實現偽AOP寫LOG功能,quartz.netaop

關於Quartz.NET作業調度框架的一點小小的封裝,實現偽AOP寫LOG功能,quartz.netaop

編輯:C#入門知識

關於Quartz.NET作業調度框架的一點小小的封裝,實現偽AOP寫LOG功能,quartz.netaop


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支持反復開啟與關閉。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved