Quartz.Net Cluster-DatabaseSupport-Priority

正如标题所示,文章主要是围绕Quartz.Net作业调度框架话题展开的,内容出自博主学习官方Examples的学习心得与体会,文中难免会有错误之处,还请指出得以指教。

一:触发的优先级

在往一个作业添加多个触发器之后,如果同一个作业多个触发时机在某个时间点同时触发,那么会先执行那个触发器执行的轮询呢?

触发的东西由触发器说了算,通过设置触发的Priority来解决先触发谁的问题。

可以通过一下代码设置触发的优先级(仅供参考):

         ITrigger trigger1 = TriggerBuilder.Create()
                .WithIdentity("Priority1Trigger5SecondRepeat")
                .StartAt(startTime)
                .WithSimpleSchedule(x => x.WithRepeatCount(1).WithIntervalInSeconds(5))
                .WithPriority(1)
                .ForJob(job)
                .Build();

设置的数值越大,相应的触发优先级就越高。

二:服务端与客户端

Quart.Net可以在客户端定义轮询作业,在服务端进行调度。

首先定义服务端,如下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using Quartz.Impl;
using Common.Logging;
using System.Collections.Specialized;

namespace Quartz.Examples
{
    /// <author>Bill Kratzer</author>
    /// <author>Marko Lahma (.NET)</author>
    public class RemoteServerExample : IExample
    {
        public string Name
        {
            get { return GetType().Name; }
        }

        /// <summary>
        /// This example will start a server that will allow clients to remotely schedule jobs.
        /// </summary>
        /// <author>  James House, Bill Kratzer
        /// </author>
        public virtual void Run()
        {
            ILog log = LogManager.GetLogger(typeof(RemoteServerExample));

            NameValueCollection properties = new NameValueCollection();
            properties["quartz.scheduler.instanceName"] = "RemoteServer";

            // set thread pool info
            properties["quartz.threadPool.type"] = "Quartz.Simpl.SimpleThreadPool, Quartz";
            properties["quartz.threadPool.threadCount"] = "5";
            properties["quartz.threadPool.threadPriority"] = "Normal";

            // set remoting exporter
            properties["quartz.scheduler.exporter.type"] = "Quartz.Simpl.RemotingSchedulerExporter, Quartz";
            properties["quartz.scheduler.exporter.port"] = "555";
            properties["quartz.scheduler.exporter.bindName"] = "QuartzScheduler";
            properties["quartz.scheduler.exporter.channelType"] = "tcp";
            properties["quartz.scheduler.exporter.channelName"] = "httpQuartz";
            // reject non-local requests
            properties["quartz.scheduler.exporter.rejectRemoteRequests"] = "true";

            ISchedulerFactory sf = new StdSchedulerFactory(properties);
            IScheduler sched = sf.GetScheduler();

            log.Info("------- Initialization Complete -----------");

            log.Info("------- Not scheduling any Jobs - relying on a remote client to schedule jobs --");

            log.Info("------- Starting Scheduler ----------------");

            // start the schedule
            sched.Start();

            log.Info("------- Started Scheduler -----------------");

            log.Info("------- Waiting 5 minutes... ------------");

            // wait to give our jobs a chance to run
            try
            {
                Thread.Sleep(TimeSpan.FromMinutes(5));
            }
            catch (ThreadInterruptedException)
            {
            }

            // shut down the scheduler
            log.Info("------- Shutting Down ---------------------");
            sched.Shutdown(true);
            log.Info("------- Shutdown Complete -----------------");

            SchedulerMetaData metaData = sched.GetMetaData();
            log.Info("Executed " + metaData.NumberOfJobsExecuted + " jobs.");
        }

    }
}

Server

通过绑定的路径,端口,以及协议。等待客户端的接入。

客户端:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Quartz.Impl;
using System.Collections.Specialized;
using Common.Logging;

namespace Quartz.Examples
{
    /// <summary>
    /// This example is a client program that will remotely
    /// talk to the scheduler to schedule a job.   In this
    /// example, we will need to use the JDBC Job Store.  The
    /// client will connect to the JDBC Job Store remotely to
    /// schedule the job.
    ///
    /// 在客户端绑定作业连接到服务端调度作业
    /// 1.先开启服务器进行监听
    /// 2.再开启客户端发送作业到服务器进行调度
    /// </summary>
    /// <author>James House</author>
    /// <author>Bill Kratzer</author>
    /// <author>Marko Lahma (.NET)</author>
    public class RemoteClientExample : IExample
    {
        public virtual void Run()
        {
            ILog log = LogManager.GetLogger(typeof(RemoteClientExample));

            NameValueCollection properties = new NameValueCollection();
            properties["quartz.scheduler.instanceName"] = "RemoteClient";

            // set thread pool info
            properties["quartz.threadPool.type"] = "Quartz.Simpl.SimpleThreadPool, Quartz";
            properties["quartz.threadPool.threadCount"] = "5";
            properties["quartz.threadPool.threadPriority"] = "Normal";

            // set remoting exporter
            properties["quartz.scheduler.proxy"] = "true";
            properties["quartz.scheduler.proxy.address"] = "tcp://127.0.0.1:555/QuartzScheduler";

            // First we must get a reference to a scheduler
            ISchedulerFactory sf = new StdSchedulerFactory(properties);
            IScheduler sched = sf.GetScheduler();

            // define the job and ask it to run

            IJobDetail job = JobBuilder.Create<SimpleJob>()
                .WithIdentity("remotelyAddedJob", "default")
                .Build();

            JobDataMap map = job.JobDataMap;
            map.Put("msg", "Your remotely added job has executed!");

            ITrigger trigger = TriggerBuilder.Create()
                .WithIdentity("remotelyAddedTrigger", "default")
                .ForJob(job.Key)
                .WithCronSchedule("/5 * * ? * *")
                .Build();

            // schedule the job
            sched.ScheduleJob(job, trigger);

            log.Info("Remote job scheduled.");
        }

        public string Name
        {
            get { return null; }
        }
    }
}

Cilent

绑定和Server配置信息指定的路径端口协议等连接到服务端,在服务端进行调度实例的调度操作。

注意:先开启Server端,再启动服务端。

三:数据库支持

在调度作业很多之后,我们需要把这些调度数据管理起来,日积月累之后通过人工的方式明显不够明智,所以,由数据库来保存这些调度数据是更好的选择。

官方提供的各种数据库脚本下载地址:

https://github.com/quartznet/quartznet/tree/master/database/tables

在代码中的操作需要留意几个地方:

            properties["quartz.jobStore.tablePrefix"] = "QRTZ_";// 数据库表名的前缀
            properties["quartz.jobStore.clustered"] = "false";//是否群集
            // properties["quartz.jobStore.lockHandler.type"] = "Quartz.Impl.AdoJobStore.UpdateLockRowSemaphore, Quartz";

            // 指定Sqlserver数据库引擎
            properties["quartz.jobStore.driverDelegateType"] = "Quartz.Impl.AdoJobStore.SqlServerDelegate, Quartz";
            //一下三个关于数据源为"quartz"根据自己的数据库名称而定
            properties["quartz.jobStore.dataSource"] = "quartz";//我的数据库取名为quartz,这里填写你们自己的数据库名称
            properties["quartz.dataSource.quartz.connectionString"] = "Server=(local);Database=quartz;Trusted_Connection=True;";//我的数据库的连接字符串
            properties["quartz.dataSource.quartz.provider"] = "SqlServer-20";//我的数据库的引擎
            properties["quartz.dataSource.quartz.maxConnections"] = "5";//我的数据库最大连接数

加入你的数据库名称叫做“YouSelfDb”,那么配置信息应该写成这样:

            properties["quartz.jobStore.dataSource"] = "YouSelfDb";//我的数据库取名为quartz,这里填写你们自己的数据库名称
            properties["quartz.dataSource.YouSelfDb.connectionString"] = "Server=(local);Database=quartz;Trusted_Connection=True;";//我的数据库的连接字符串
            properties["quartz.dataSource.YouSelfDb.provider"] = "SqlServer-20";//我的数据库的引擎
            properties["quartz.dataSource.YouSelfDb.maxConnections"] = "5";/

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using Quartz.Impl;
using System.Collections.Specialized;
using Common.Logging;

namespace Quartz.Examples
{
    public class ClusterExample : IExample
    {
        private static readonly ILog log = LogManager.GetLogger(typeof(ClusterExample));

        public virtual void Run(bool inClearJobs, bool inScheduleJobs)
        {
            NameValueCollection properties = new NameValueCollection();

            properties["quartz.scheduler.instanceName"] = "TestScheduler";
            properties["quartz.scheduler.instanceId"] = "instance_one";
            properties["quartz.threadPool.type"] = "Quartz.Simpl.SimpleThreadPool, Quartz";
            properties["quartz.threadPool.threadCount"] = "5";
            properties["quartz.threadPool.threadPriority"] = "Normal";
            properties["quartz.jobStore.misfireThreshold"] = "60000";
            properties["quartz.jobStore.type"] = "Quartz.Impl.AdoJobStore.JobStoreTX, Quartz";
            properties["quartz.jobStore.useProperties"] = "false";

            properties["quartz.jobStore.tablePrefix"] = "QRTZ_";
            properties["quartz.jobStore.clustered"] = "false";
            // if running SQLite we need this
            // properties["quartz.jobStore.lockHandler.type"] = "Quartz.Impl.AdoJobStore.UpdateLockRowSemaphore, Quartz";
            properties["quartz.jobStore.driverDelegateType"] = "Quartz.Impl.AdoJobStore.SqlServerDelegate, Quartz";
            //一下三个关于数据源为"quartz"根据自己的数据库名称而定
            properties["quartz.jobStore.dataSource"] = "quartz";
            properties["quartz.dataSource.quartz.connectionString"] = "Server=(local);Database=quartz;Trusted_Connection=True;";
            properties["quartz.dataSource.quartz.provider"] = "SqlServer-20";
            properties["quartz.dataSource.quartz.maxConnections"] = "5";//最大连接数
            // First we must get a reference to a scheduler
            ISchedulerFactory sf = new StdSchedulerFactory(properties);
            IScheduler sched = sf.GetScheduler();

            if (inClearJobs)
            {
                log.Warn("***** Deleting existing jobs/triggers *****");
                sched.Clear();
            }

            log.Info("------- Initialization Complete -----------");

            if (inScheduleJobs)
            {
                log.Info("------- Scheduling Jobs ------------------");

                string schedId = sched.SchedulerInstanceId;

                int count = 1;

                IJobDetail job = JobBuilder.Create<SimpleRecoveryJob>()
                    .WithIdentity("job_" + count, schedId) // put triggers in group named after the cluster node instance just to distinguish (in logging) what was scheduled from where
                    .RequestRecovery() // ask scheduler to re-execute this job if it was in progress when the scheduler went down...
                    .Build();

                ISimpleTrigger trigger = (ISimpleTrigger)TriggerBuilder.Create()
                                                              .WithIdentity("triger_" + count, schedId)
                                                              .StartAt(DateBuilder.FutureDate(1, IntervalUnit.Second))
                                                              .WithSimpleSchedule(x => x.WithRepeatCount(20).WithInterval(TimeSpan.FromSeconds(5)))
                                                              .Build();

                log.InfoFormat("{0} will run at: {1} and repeat: {2} times, every {3} seconds", job.Key, trigger.GetNextFireTimeUtc(), trigger.RepeatCount, trigger.RepeatInterval.TotalSeconds);

                count++;

                job = JobBuilder.Create<SimpleRecoveryJob>()
                    .WithIdentity("job_" + count, schedId) // put triggers in group named after the cluster node instance just to distinguish (in logging) what was scheduled from where
                    .RequestRecovery() // ask scheduler to re-execute this job if it was in progress when the scheduler went down...
                    .Build();

                trigger = (ISimpleTrigger)TriggerBuilder.Create()
                                               .WithIdentity("triger_" + count, schedId)
                                               .StartAt(DateBuilder.FutureDate(2, IntervalUnit.Second))
                                               .WithSimpleSchedule(x => x.WithRepeatCount(20).WithInterval(TimeSpan.FromSeconds(5)))
                                               .Build();

                log.Info(string.Format("{0} will run at: {1} and repeat: {2} times, every {3} seconds", job.Key, trigger.GetNextFireTimeUtc(), trigger.RepeatCount, trigger.RepeatInterval.TotalSeconds));
                sched.ScheduleJob(job, trigger);

                count++;

                job = JobBuilder.Create<SimpleRecoveryStatefulJob>()
                    .WithIdentity("job_" + count, schedId) // put triggers in group named after the cluster node instance just to distinguish (in logging) what was scheduled from where
                    .RequestRecovery() // ask scheduler to re-execute this job if it was in progress when the scheduler went down...
                    .Build();

                trigger = (ISimpleTrigger)TriggerBuilder.Create()
                                               .WithIdentity("triger_" + count, schedId)
                                               .StartAt(DateBuilder.FutureDate(1, IntervalUnit.Second))
                                               .WithSimpleSchedule(x => x.WithRepeatCount(20).WithInterval(TimeSpan.FromSeconds(3)))
                                               .Build();

                log.Info(string.Format("{0} will run at: {1} and repeat: {2} times, every {3} seconds", job.Key, trigger.GetNextFireTimeUtc(), trigger.RepeatCount, trigger.RepeatInterval.TotalSeconds));
                sched.ScheduleJob(job, trigger);

                count++;

                job = JobBuilder.Create<SimpleRecoveryJob>()
                    .WithIdentity("job_" + count, schedId) // put triggers in group named after the cluster node instance just to distinguish (in logging) what was scheduled from where
                    .RequestRecovery() // ask scheduler to re-execute this job if it was in progress when the scheduler went down...
                    .Build();

                trigger = (ISimpleTrigger)TriggerBuilder.Create()
                                               .WithIdentity("triger_" + count, schedId)
                                               .StartAt(DateBuilder.FutureDate(1, IntervalUnit.Second))
                                               .WithSimpleSchedule(x => x.WithRepeatCount(20).WithInterval(TimeSpan.FromSeconds(4)))
                                               .Build();

                log.Info(string.Format("{0} will run at: {1} & repeat: {2}/{3}", job.Key, trigger.GetNextFireTimeUtc(), trigger.RepeatCount, trigger.RepeatInterval));
                sched.ScheduleJob(job, trigger);

                count++;

                job = JobBuilder.Create<SimpleRecoveryJob>()
                    .WithIdentity("job_" + count, schedId) // put triggers in group named after the cluster node instance just to distinguish (in logging) what was scheduled from where
                    .RequestRecovery() // ask scheduler to re-execute this job if it was in progress when the scheduler went down...
                    .Build();

                trigger = (ISimpleTrigger)TriggerBuilder.Create()
                                               .WithIdentity("triger_" + count, schedId)
                                               .StartAt(DateBuilder.FutureDate(1, IntervalUnit.Second))
                                               .WithSimpleSchedule(x => x.WithRepeatCount(20).WithInterval(TimeSpan.FromMilliseconds(4500)))
                                               .Build();

                log.Info(string.Format("{0} will run at: {1} & repeat: {2}/{3}", job.Key, trigger.GetNextFireTimeUtc(), trigger.RepeatCount, trigger.RepeatInterval));
                sched.ScheduleJob(job, trigger);
            }

            // jobs don‘t start firing until start() has been called...
            log.Info("------- Starting Scheduler ---------------");
            sched.Start();
            log.Info("------- Started Scheduler ----------------");

            log.Info("------- Waiting for one hour... ----------");

            Thread.Sleep(TimeSpan.FromHours(1));

            log.Info("------- Shutting Down --------------------");
            sched.Shutdown();
            log.Info("------- Shutdown Complete ----------------");
        }

        public string Name
        {
            get { throw new NotImplementedException(); }
        }

        public void Run()
        {
            bool clearJobs = true;
            bool scheduleJobs = true;
            /* TODO
            for (int i = 0; i < args.Length; i++)
            {
                if (args[i].ToUpper().Equals("clearJobs".ToUpper()))
                {
                    clearJobs = true;
                }
                else if (args[i].ToUpper().Equals("dontScheduleJobs".ToUpper()))
                {
                    scheduleJobs = false;
                }
            }
            */
            ClusterExample example = new ClusterExample();
            example.Run(clearJobs, scheduleJobs);
        }
    }
}

DbSupport

打开你的数据库YouSelfDb,这个库下面有相应的表存储相应的调度数据:比如

QRTZ_TRIGGERS表将会保存具体的作业调度信息,包括作业,触发器等信息。

QRTZ_JOB_DETAILS表保存作业数据。

QRTZ_SIMPLE_TRIGGERS表保存SimpleTirgger类的触发器数据。

QRTZ_CALENDARS表保存日历触发器数据。

QRTZ_CRON_TRIGGERS表保存Cron表达式的tirgger触发器数据。

等等。。。。。(不一 一例举了,你们自己看吧)

四,集群Cluster

在我看来所谓的集群就是一堆机器一起工作。

一堆作业一起调度,但作业与作业之间不会相互干扰,一个作业的停止运行不会影响到另外一个作业的正常工作。

通过properties["quartz.jobStore.clustered"] = "true";启动集群

而集群的代码例子和上面的DbSupport是一样的,这里不再重复贴出。

时间: 2024-10-25 13:57:51

Quartz.Net Cluster-DatabaseSupport-Priority的相关文章

使用sqlserver搭建高可用双机热备的Quartz集群部署【附源码】

一般拿Timer和Quartz相比较的,简直就是对Quartz的侮辱,两者的功能根本就不在一个层级上,如本篇介绍的Quartz强大的集群机制,可以采用基于 sqlserver,mysql的集群方案,当然还可以在第三方插件的基础上实现quartz序列化到热炒的mongodb,redis,震撼力可想而知,接下来本篇就和大家聊 一聊怎么搭建基于sqlserver的quartz集群,实现这么一种双机热备的强大功能. 一:下载sqlserver版的建表脚本   首先大家可以通过github上搜索quart

双机热备的Quartz集群

sqlserver搭建高可用双机热备的Quartz集群部署[附源码] 一般拿Timer和Quartz相比较的,简直就是对Quartz的侮辱,两者的功能根本就不在一个层级上,如本篇介绍的Quartz强大的集群机制,可以采用基于 sqlserver,mysql的集群方案,当然还可以在第三方插件的基础上实现quartz序列化到热炒的mongodb,redis,震撼力可想而知,接下来本篇就和大家聊 一聊怎么搭建基于sqlserver的quartz集群,实现这么一种双机热备的强大功能. 一:下载sqlse

集群式Quartz定时任务框架实践

在日常开发汇总,经常会遇到需要定时任务的场景,简单的,可以使用Spring的定时任务调度框架,也可以使用Quartz.无论使用哪种,都需要解决一个问题,那就是集群问题.一般情况下,定时任务能且仅能运行于一台应用实例上. 前提 本文工程基于spring boot 2.1.7.RELEASE 工程配置  一.pom依赖 如下图所示: 二.yml配置 yml配置如下图所示: 三.quartz.properties quartz相关属性配置如图: 注意: 1.重中之重,要设置org.quartz.job

Quartz Trigger Priority 触发器优先级

Quartz Trigger Priority 触发器优先级 当多个触发器在一个相同的时间内触发,并且调度引擎中的资源有限的情况下,那么具有较高优先级的触发器先触发. 需要将配置文件中的org.quartz.threadPool.threadCount = 1设置为1,这样能更好的测试出效果. package com.gary.operation.jobdemo.example14; import static org.quartz.DateBuilder.futureDate; import

分布式调度QUARTZ+SPRING

使用SPRING的定时任务框架,如果是在分布式的环境下,由于有多台节点,会产生相同的任务,会被多个节点执行,这时需引入分布式的QUARTZ. 触发器:存放时间排程 任务:蔟业务代码 排程器:负责调度,即在指定的时间执行对应的任务 如果是分布式QUARTZ,则各个节点会上报任务,存到数据库中,执行时会从数据库中取出触发器来执行,如果触发器的名称和执行时间相同,则只有一个节点去执行此任务. 如果此节点执行失败,则此任务则会被分派到另一节点执行. quartz.properties # =======

spring quartz 分布式任务计划

通过maven管理的spring mvc工程,且已经成功连接数据库. 数据库表结构 /*Table structure for table `qrtz_calendars` */ DROP TABLE IF EXISTS `qrtz_calendars`; CREATE TABLE `qrtz_calendars` ( `SCHED_NAME` varchar(120) NOT NULL, `CALENDAR_NAME` varchar(200) NOT NULL, `CALENDAR` blo

spring与quartz整合实现分布式动态创建,删除,改变执行时间定时任务(mysql数据库)

背景:因为在项目中用到了定时任务,当时想到了spring的quartz,写完发现费了很大功夫,光是整合就花了一上午,其中最大的问题就是版本问题,项目中用的是spring3.2.8的版本,查阅发现,3.0以上的版本需要使用quartz2.X以上版本,我就去官网下载了2.1.7的quartz,结果发现jar包与spring冲突,最后使用了quartz1.6.0版本. spring与quartz整合第一步需要导jar包,这个在百度搜下quartz的jar,下载一个 第二步:分布式定时任务,是基于数据库

quartz集群调度机制调研及源码分析---转载

quartz2.2.1集群调度机制调研及源码分析引言quartz集群架构调度器实例化调度过程触发器的获取触发trigger:Job执行过程:总结:附: 引言 quratz是目前最为成熟,使用最广泛的java任务调度框架,功能强大配置灵活.在企业应用中占重要地位.quratz在集群环境中的使用方式是每个企业级系统都要考虑的问题.早在2006年,在ITeye上就有一篇关于quratz集群方案的讨论:http://www.iteye.com/topic/40970 ITeye创始人@Robbin在8楼

定时组件quartz系列&lt;二&gt;quartz的集群原理

1.基本信息: Quartz是一个开源的作业调度框架,它完全由java写成,并设计用于J2Se和J2EE应用中.它提供了巨大的灵活性而不牺牲简单性.你能够用它 来为执行一个作业而创建简单的或复杂的调度.它有很多特征,如:数据库支持,集群,插件,EJB作业预构建,JavaMail及其它,支持cron- like表达式等等.其中集群配置一般比较复杂,那么在Quartz中如何配置它的集群特性呢? 2 Quartz的集群配置:      2.1 实现集群的基本原理           Quartz是通过

定时组件quartz系列&lt;三&gt;quartz调度机制调研及源码分析

quartz2.2.1集群调度机制调研及源码分析引言quartz集群架构调度器实例化调度过程触发器的获取触发trigger:Job执行过程:总结:附: 引言 quratz是目前最为成熟,使用最广泛的java任务调度框架,功能强大配置灵活.在企业应用中占重要地位.quratz在集群环境中的使用方式是每个企业级系统都要考虑的问题.早在2006年,在ITeye上就有一篇关于quratz集群方案的讨论:http://www.iteye.com/topic/40970 ITeye创始人@Robbin在8楼