The Quartz. NET job scheduling framework is encapsulated to implement the LOG writing function of pseudo AOP. quartz. netaop
Quartz. NET is a very powerful job scheduling framework, suitable for various scheduled business processing, similar to the WINDOWS built-in task scheduler, among them, using Cron expressions to implement various timing trigger conditions is what I think is the most surprising.
Quartz. NET mainly uses the following classes:
Isched -- Scheduler
IJobDetail -- job task
ITrigger -- trigger
If we use Timer to write similar scheduled task execution programs, we should have the following ideas: (the purpose is to let everyone understand Quartz. NET)
ScheduleTimer -- Timer, which is executed once per second;
TriggerClass -- determines whether a job task needs to be executed. Each time ScheduleTimer executes a job, a new thread should call the NeedExecute method or attribute of the TriggerClass member;
JobClass -- the specific job task class, TriggerClass. If TriggerClass. NeedExecute returns true, the JobClass member Execute method should be executed;
Well, there are many introductions to Quartz. NET. I will not talk about it here. The following describes how to implement the LOG writing function of pseudo AOP.
I don't know about AOP. Click here to learn more.
Quartz. NET, although it has integrated the log writing function of log4net, you only need to configure it in Config, but I think the logs written in the framework do not meet my requirements, therefore, I need to write logs based on certain conditions according to the actual business needs, so this article is available.
The following is a Job package class or a Job proxy class. The complete code is as follows:
[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; // busy judge try {jobInner. execute (context);} catch (Exception ex) {Master. writeMsg (context. jobDetail. key + "execution exception:" + ex. message + Environment. newLine + ex. stackTrace, true, true);} Interlocked. exchange (ref syncFlag, 0); // cancel busy}
The code is very simple, and most people can understand it. I just want to focus on it:
1. the syncFlag static field is used to mark whether it is busy or not. 1 indicates not busy, others indicate busy, Interlocked. increment and Interlocked. exchange is used at the atomic level to ensure that only one thread can operate at a time, similar to the exclusive lock in SQL, which is a bit the same as lock but different, if you use lock to wrap the entire execution with braces, the lock range is relatively wide and difficult to control, while Interlocked only needs to be exclusive when needed, and the exclusive time is very short, most of the time is normal and easier to control, which is why I like to use it.
2. why do I mark as busy or not busy? The reason is that I must ensure that the business logic executed each time can be completed, instead of failing to complete the execution. The next execution point will arrive again, this results in multiple or even repeated executions.
2. Why package? The reason is that I don't want to write try catch exception capture and busy and not busy judgment in every Job class. In this way, common classes only need to focus on business processing. As for the wrapped class, you do not have to use the IJob interface. You can define various interfaces, but you must have no constructor. Otherwise, you cannot create an instance of the wrapped class.
Through the above explanation, everyone should understand. Below is a Job manager Job management class encapsulated to facilitate integrated management. The complete code is as follows: (the code is relatively simple and I will not describe it again)
Public class JobManager {private IScheduler schedtionary = null; private int schedulerState = 0; public Dictionary <string, JobWithTrigger> JobTriggers {get; private set;} private IScheduler GetAScheduler () {var stdSchedulerFactory = new StdSchedulerFactory (); scheduler = stdSchedulerFactory. getschedmanager (); return scheduler;} public JobManager () {scheduler = GetAScheduler (); JobTriggers = new Dic Tionary <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> (). withIdenti Ty (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 JobWithTri Gger [] jts) {if (scheduler. isShutdown) {scheddown = 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) {schedys. 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: not started, 1: started, 2: paused, 3: restored, -1: Stop} [MethodImpl (MethodImplOptions. synchronized)] public void Start () {if (schedulerState> 0) retu Rn; scheduler. Start (); schedulerState = 1; Master. WriteMsg ("The AutoTimingExecSystem program has been started, and all tasks Start as planned. ", False, true);} [MethodImpl (MethodImplOptions. synchronized)] public void Stop () {if (schedulerState <= 0) return; schedstate. clear (); scheduler. shutdown (); schedulerState =-1; Master. writeMsg ("The AutoTimingExecSystem program is stopped, and all tasks are stopped. ", False, true);} [MethodImpl (MethodImplOptions. Synchronized)] public void Pause () {if (schedulerState! = 1) return; scheduler. PauseAll (); schedulerState = 2; Master. WriteMsg ("all tasks are canceled or paused. ", False, true);} [MethodImpl (MethodImplOptions. Synchronized)] public void Resume () {if (schedulerState! = 2) return; scheduler. ResumeAll (); schedulerState = 1; Master. WriteMsg ("all tasks resume execution. ", False, true );}}
JobWithTrigger: task and trigger association class
[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; } }
The usage is simple. The sample code is as follows:
Var jobManager = new JobManager (); var jt = jobManager. CreateJobWithTrigger <JobWraper <TestJob> ("0/5 ****? "); // Save or display the jt on the task interface... jobManager. scheduleJobs (JobWithTrigger's KEY array or JobWithTrigger object) jobManager. start (); jobManager. stop ();
JobManager supports repeated enabling and disabling.