第十节: 利用SQLServer实现Quartz的持久化和双机热备的集群模式 :

背景: 默认情况下,Quartz.Net作业是持久化在内存中的,即 quartz.jobStore.type = "Quartz.Simpl.RAMJobStore, Quartz",这种模式有以下弊端:

① 想在A服务器上控制B服务器上已经发布了的job和trigger不方便;

② 很难实现Web端(寄宿在IIS上)管理作业,客户端(发布成服务)的这种模式。

③ 最大弊端就是一旦服务器宕机或者重启,调度器Schdeuler对应的所有作业(job、trigger及其对应关系)将丢失,不得不重新发布;

 解决方案:

  针对问题1. 可以借助Remote代理的模式,通过TCP协议在A服务器上直接获取B服务器上的Scheduler,然后进行操作。(详情点击)

  针对问题2. 客户端作为Server端进行调度的执行,Web端通过Remote模式获取客户端的中的Scheduler,然后进行作业的管理,问题是一旦客户端端挂机,Web端是连接不上的

  针对问题3. 无论代理还是不代理,只要Server端一挂机,保存在内存中的作业都会丢失,所以这个时候,我们需要另辟蹊径,将作业持久化进行迁移,比如迁移到数据库中,这样话,即使服务器宕机,数据库中存储的作业信息仍然存在,下次只需要开启Scheduler即可,无须配置job和trigger了,同时也解决了上述问题1和问题2,即都可以直接修改数据库即可。

该章节也是为开篇提出的目标三铺最后一道路,下面着重介绍持久化SQLServer数据库。

步骤1:准备数据库脚本。

  下载地址为:https://github.com/quartznet/quartznet/blob/master/database/tables/tables_sqlServer.sql,执行后的数据库如下图:

重点介绍一下以上表的含义:

  qrtz_blob_triggers : 以Blob 类型存储的触发器。 

  qrtz_calendars:存放日历信息, quartz可配置一个日历来指定一个时间范围。 

  qrtz_cron_triggers:存放cron类型的触发器。 

  qrtz_fired_triggers:存放已触发的触发器。 

  qrtz_job_details:存放一个jobDetail信息。 

  qrtz_job_listeners:job**监听器**。 

  qrtz_locks: 存储程序的悲观锁的信息(假如使用了悲观锁)。 

  qrtz_paused_trigger_graps:存放暂停掉的触发器。 

  qrtz_scheduler_state:调度器状态。 

  qrtz_simple_triggers:存放简单触发器的信息。 

  qrtz_trigger_listeners:触发器监听器。 

  qrtz_triggers:将Trigger和job进行关联的表。

注:cron方式需要用到的4张数据表: qrtz_cron_triggers,qrtz_fired_triggers,qrtz_job_details,qrtz_triggers。

步骤2:代码进行持久化数据库配置

  需要配置的信息有SQLServer版本、数据库连接字符串、存储类型、数据源名称、驱动类型,代码如下:

复制代码

 1            var properties = new NameValueCollection();2             //SQLServer版本3             properties.Add("quartz.dataSource.myDS.provider", "SqlServer-20");4             //表名前缀(可有可无)5             //properties.Add("quartz.jobStore.tablePrefix", "QRTZ_");6             //数据库连接字符串7             properties.Add("quartz.dataSource.myDS.connectionString", "Data Source=.;Initial Catalog=quartz;User ID=sa;Password=123456");8             //properties.Add("quartz.dataSource.myDS.connectionString", "Server =.;Database = quartz;Trusted_Connection =True;"); 9             //JobStore设置(JobStoreTX: 带有事务;JobStoreCMT:不带有事务)
10             //存储类型
11             properties.Add("quartz.jobStore.type", "Quartz.Impl.AdoJobStore.JobStoreTX, Quartz");
12             //数据源名称
13             properties.Add("quartz.jobStore.dataSource", "myDS");
14             //驱动类型
15             properties.Add("quartz.jobStore.driverDelegateType", "Quartz.Impl.AdoJobStore.StdAdoDelegate, Quartz");

复制代码

步骤3:向数据库中持久化作业,并开启调度。

代码如下:

复制代码

 1            var factory = new StdSchedulerFactory(properties);2             IScheduler scheduler = factory.GetScheduler();3             var job = JobBuilder.Create<HelloJob4>()4                                 .WithIdentity("ypfJob1", "ypfJobGroup1")5                                 .Build();6             var trigger = TriggerBuilder.Create()7                                  .WithIdentity("ypfTrigger1", "ypfTriggerGroup1")8                                 .WithCronSchedule("/3 * * * * ?")9                                 .Build();
10             if (!scheduler.CheckExists(job.Key))
11             {
12                 scheduler.ScheduleJob(job, trigger);
13             }
14             scheduler.Start();

复制代码

运行结果为:

 

此时分析数据库中的数据:

QRTZ_CRON_TRIGGERS  表:即存放cron类型的trigger

 QRTZ_JOB_DETAILS  表:即存放job的信息

 

QRTZ_TRIGGERS 表:将Trigger和job进行关联的表

 QRTZ_FIRED_TRIGGERS 表:

 

下面做几个实验,验证持久化问题:

 实验1:去掉代码中job和trigger的创建及关联,直接进行调度器的启动。

 

实验结果:调度正常按照每3s执行一次,证明作业持久化数据库成功。

 

实验2:修改数据库中的cron表达式为每5s执行一次,然后保持实验1中的代码注释,运行代码。

 

实验结果:调度变为每隔5s执行一次了,证明作业持久化数据库成功。

 

实验3:我们在上面的数据库表中发现一个现象,第一个字段都为Sched_Name,即调度器的名称,而且默认都为QuartzScheduler,那么如何增加多个不同名称的调度器呢?获取的时候又是怎么获取指定的调度器呢?都是通过下面的这句代码配置:

properties.Add("quartz.scheduler.instanceName", "Ypf1Scheduler");

 分享完整代码:

复制代码

            var properties = new NameValueCollection();//SQLServer版本properties.Add("quartz.dataSource.myDS.provider", "SqlServer-20");//表名前缀(可有可无)//properties.Add("quartz.jobStore.tablePrefix", "QRTZ_");//数据库连接字符串properties.Add("quartz.dataSource.myDS.connectionString", "Data Source=.;Initial Catalog=quartz;User ID=sa;Password=123456");//properties.Add("quartz.dataSource.myDS.connectionString", "Server =.;Database = quartz;Trusted_Connection =True;"); //JobStore设置(JobStoreTX: 带有事务;JobStoreCMT:不带有事务)//存储类型properties.Add("quartz.jobStore.type", "Quartz.Impl.AdoJobStore.JobStoreTX, Quartz");//数据源名称properties.Add("quartz.jobStore.dataSource", "myDS");//驱动类型properties.Add("quartz.jobStore.driverDelegateType", "Quartz.Impl.AdoJobStore.StdAdoDelegate, Quartz");//Scheuler的名称,用于处理多个调度器的情况(指定和获取都是用这句代码,如果不指定的话,默认均为QuartzScheduler名称){properties.Add("quartz.scheduler.instanceName", "Ypf1Scheduler");}var factory = new StdSchedulerFactory(properties);IScheduler scheduler = factory.GetScheduler();var job = JobBuilder.Create<HelloJob4>().WithIdentity("ypfJob1", "ypfJobGroup1").Build();var trigger = TriggerBuilder.Create().WithIdentity("ypfTrigger1", "ypfTriggerGroup1").WithCronSchedule("/10 * * * * ?").Build();//Scheduler只要存在相同的job名称,将不在关联 (这里需要根据实际要求来处理)if (!scheduler.CheckExists(job.Key)){scheduler.ScheduleJob(job, trigger);}scheduler.Start();

复制代码

发现数据中上述的几张表中多了一条数据:

 

 

  PS:前面有博友【 搵中求胜】给我留言提示集群的问题,这里借助他的话给大家一个提醒:

  在使用 Quartz.Impl.AdoJobStore 做集群时,一旦出现连接超时或者底层的SQL错误,这个Job将彻底堵住,即使数据库连接恢复该JOB也得不到恢复,继承自IJob的Execute方法将不会被调用。

因此,必须有一个Timer对这些超时未执行的Job做重置或者移除再加入(切误参考网上DEMO做一个Manager继承IJob,因为Manager也被堵住了)

 

二. 双机热备的集群模式

集群的两种形式:

  1.读写分离:即master - slave,在SQLServer通过“发布-订阅”来实现,写是落库到master,读从slave中,一个主多个从。

  2.双机热备:即一主多备,高可用,主挂掉了,备会自动顶上去, Quartz.Net集群采用的就是这种形式(备用服务启动,最短大约需要7.5s)。

配置代码在持久化的基础上多了两句:

  properties["quartz.jobStore.clustered"] = "true";
  properties["quartz.scheduler.instanceId"] = "AUTO";

 下面分享完成的一段代码:

复制代码

 1   var properties = new NameValueCollection();2 3             properties["quartz.dataSource.sqlserver.provider"] = "SqlServer-20";4             properties["quartz.dataSource.sqlserver.connectionString"] = @"Data Source=.;Initial Catalog=quartz;User ID=sa;Password=123456";5             properties["quartz.jobStore.type"] = "Quartz.Impl.AdoJobStore.JobStoreTX, Quartz";6             //注意这个名字改为了sqlserver,上面的都要跟着改,也可以改为别的名7             properties["quartz.jobStore.dataSource"] = "sqlserver";8             properties["quartz.jobStore.driverDelegateType"] = "Quartz.Impl.AdoJobStore.SqlServerDelegate, Quartz";9 
10             //cluster 集群指定
11             properties["quartz.jobStore.clustered"] = "true";
12             properties["quartz.scheduler.instanceId"] = "AUTO";
13 
14             //Scheuler的名称,用于处理多个调度器的情况(指定和获取都是用这句代码,如果不指定的话,默认均为QuartzScheduler名称)
15             {
16                 properties.Add("quartz.scheduler.instanceName", "QuartzSchoolScheduler");
17             }
18 
19             var factory = new StdSchedulerFactory(properties);
20             var scheduler = factory.GetScheduler();
21             var job = JobBuilder.Create<HelloJob4>()
22                                 .WithIdentity("job3", "jobGroup3")
23                                 .Build();
24             //trigger   2s执行一次
25             var trigger = TriggerBuilder.Create()
26                                         .WithIdentity("trigger3", "triggerGroup3")
27                                         .WithSimpleSchedule(x => x.WithIntervalInSeconds(2).RepeatForever())
28                                         .Build();
29             var isExists = scheduler.CheckExists(job.Key);
30             if (!isExists)
31             {
32                 //开始调度
33                 scheduler.ScheduleJob(job, trigger);
34             }
35             scheduler.Start();

复制代码

数据库表的变化:

 QRTZ_SIMPROP_TRIGGERS 表:  (与cron的trigger存放的位置不同)

 QRTZ_JOB_DETAILS 表:

 

 QRTZ_TRIGGERS 表:

 QRTZ_FIRED_TRIGGERS 表:

 

运行结果:

   生成一下代码,直接在bin文件里打开,然后再打开一个,发现第一个正常运行,第二个不运行。

 

  关闭第一个客户端,7.5s后发现第二个正常启动运行,验证双机热备。

 

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/438775.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

第十节: 利用SQLServer实现Quartz的持久化和双机热备的集群模式

背景&#xff1a; 默认情况下&#xff0c;Quartz.Net作业是持久化在内存中的&#xff0c;即 quartz.jobStore.type "Quartz.Simpl.RAMJobStore, Quartz"&#xff0c;这种模式有以下弊端&#xff1a; ① 想在A服务器上控制B服务器上已经发布了的job和trigger不方便&a…

任务调度开源框架Quartz动态添加、修改和删除定时任务

Quartz 是个开源的作业调度框架&#xff0c;为在 Java 应用程序中进行作业调度提供了简单却强大的机制。Quartz框架包含了调度器监听、作业和触发器监听。你可以配置作业和触发器监听为全局监听或者是特定于作业和触发器的监听。Quartz 允许开发人员根据时间间隔&#xff08;或…

第十二节: 总结Quartz.Net几种部署模式(IIS、Exe、服务部署【借助TopSelf、服务类】)

一. IIS部署 比如在MVC框架中集成了Quartz.Net定时调度&#xff0c;此时该调度系统会随着MVC框架被挂在IIS下&#xff0c;IIS会进程回收&#xff0c;所以大部分开发都会遇到Quartz挂在IIS下一段时间不好用。 补充&#xff1a;IIS可以设置定时自动回收&#xff0c;默认回收是174…

[你必须知道的.NET] 第二回:对抽象编程:接口和抽象类

本文将介绍以下内容&#xff1a; • 面向对象思想&#xff1a;多态 • 接口 • 抽象类 1. 引言 在我之前的一篇post《抽象类和接口的谁是谁非》中&#xff0c;和同事管伟的讨论&#xff0c;得到很多朋友的关注&#xff0c;因为是不成体系的论道&#xff0c;所以给大家了解造…

Linux 启动失败 磁盘阵列,组建RAID5重启系统,出现md127的解决办法

我这里在组建RAID5之后重启了系统&#xff0c;当时/dev/md0变成了/dev/md127。出现这问题主要是我没有配置生成/etc/mdadm.conf文件&#xff0c;如果哪位伙伴出现类似我这种情况&#xff0c;而不是通过我这种方法解决的&#xff0c;希望能分享一下你的解决方法&#xff0c;谢谢…

[你必须知道的.NET] 第三回:历史纠葛:特性和属性

本文将介绍以下内容&#xff1a; • 定制特性的基本概念和用法 • 属性与特性的区别比较 • 反射的简单介绍 1. 引言 attribute是.NET框架引入的有一技术亮点&#xff0c;因此我们有必要花点时间来了解本文的内容&#xff0c;走进一个发现attribute登堂入室的入口。因为.N…

Linux的性能故障的含义,Linux排查性能故障的方法

好了&#xff0c;鉴于我们的Linux发行版现已安装&#xff0c;许多方面看起来似乎根本不“好”。今天我们将介绍为安装的Linux排查性能故障的方法。虽然今天的这篇文章明显倾向于Ubuntu&#xff0c;但我们探讨的几乎一切内容都同样适用于每个Linux发行版。要是哪些命令是专门针对…

[你必须知道的.NET] 第四回:后来居上:class和struct

本文将介绍以下内容&#xff1a; • 面向对象基本概念 • 类和结构体简介 • 引用类型和值类型区别 1. 引言 提起class和struct&#xff0c;我们首先的感觉是语法几乎相同&#xff0c;待遇却翻天复地。历史将接力棒由面向过程编程传到面向对象编程&#xff0c;class和stru…

linux shell跳板机,用shell开发跳板机

信号列表&#xff1a;在linux中和信号相关的常见命令为kill及trap命令&#xff0c;下来如何利用trap控制跳板机脚本来使用命令&#xff1a;命令&#xff1a;kill -l 和 trap -l企业实战中linux系统的重要信号及说明HUP(1) 挂起INT(2) 中断OUIT(3) 退…

[你必须知道的.NET] 第五回:深入浅出关键字---把new说透

本文将介绍以下内容&#xff1a; 面向对象基本概念new关键字深入浅出对象创建的内存管理 1. 引言 园子里好像没有或者很少把new关键字拿出来说的&#xff0c;那我就占个先机吧&#xff0c;呵呵。那么&#xff0c;我们到底有必要将一个关键字拿出来长篇大论吗&#xff1f;看来…

linux tcp server开源,GitHub - 06linux/cellnet: 高性能,简单,方便的开源服务器网络库...

cellnetcellnet是一个高性能&#xff0c;简单&#xff0c;方便的开源服务器网络库自由混合编码&#xff0c;业务代码无需调整。TCP和html5的应用都可以直接使用cellnet迅速搭建服务器框架。与Java的Netty或Mina网络库类似的Handler机制将给予强大定制功能。特性数据协议支持混合…

[你必须知道的.NET] 第六回:深入浅出关键字---base和this

本文将介绍以下内容&#xff1a; 面向对象基本概念base关键字深入浅出this关键字深入浅出1. 引言 new关键字引起了大家的不少关注&#xff0c;尤其感谢Anders Liu的补充&#xff0c;让我感觉博客园赋予的交流平台真的无所不在。所以&#xff0c;我们就有必要继续这个话题&…

[你必须知道的.NET] 第七回:品味类型---从通用类型系统开始

本文将介绍以下内容&#xff1a; .NET 基础架构概念 类型基础通用类型系统CLI、CTS、CLS的关系简述1. 引言 本文不是连环画&#xff0c;之所以在开篇以图形的形式来展示本文主题&#xff0c;其实就是想更加特别的强调这几个概念的重要性和关注度&#xff0c;同时希望从剖析其关…

[你必须知道的.NET] 第八回:品味类型---值类型与引用类型(上)-内存有理

本文将介绍以下内容&#xff1a; 类型的基本概念 值类型深入引用类型深入值类型与引用类型的比较及应用1. 引言 买了新本本&#xff0c;忙了好几天系统&#xff0c;终于开始了对值类型和引用类型做个全面的讲述了&#xff0c;本系列开篇之时就是因为想写这个主题&#xff0c;…

[你必须知道的.NET]第九回:品味类型---值类型与引用类型(中)-规则无边

接上回[第八回&#xff1a;品味类型---值类型与引用类型&#xff08;上&#xff09;&#xff0d;内存有理]的探讨&#xff0c;继续我们关注值类型和引用类型的话题。 本文将介绍以下内容&#xff1a; 类型的基本概念 值类型深入引用类型深入值类型与引用类型的比较及应用1. 引…

[你必须知道的.NET]第十回:品味类型---值类型与引用类型(下)-应用征途

本文将介绍以下内容&#xff1a; 类型的基本概念 值类型深入引用类型深入值类型与引用类型的比较及应用 [下载]&#xff1a;[类型示例代码] 1. 引言 值类型与引用类型的话题经过了两个回合&#xff08;[第八回&#xff1a;品味类型---值类型与引用类型&#xff08;上&#xf…

[你必须知道的.NET]第十一回:参数之惑---传递的艺术(上)

本文将介绍以下内容&#xff1a; 按值传递与按引用传递深论ref和out比较 参数应用浅析 1. 引言 接上回《第九回&#xff1a;品味类型---值类型与引用类型&#xff08;中&#xff09;&#xff0d;规则无边》中&#xff0c;对值类型和引用类型的讨论&#xff0c;其中关于string…

[你必须知道的.NET]第十二回:参数之惑---传递的艺术(下)

本文将介绍以下内容&#xff1a; 按值传递与按引用传递深论ref和out比较 参数应用浅析 接上篇继续&#xff0c;『第十一回&#xff1a;参数之惑---传递的艺术&#xff08;上&#xff09;』 4.2 引用类型参数的按值传递 当传递的参数为引用类型时&#xff0c;传递和操作的是指…

[你必须知道的.NET]第十三回:从Hello, world开始认识IL

本文将介绍以下内容&#xff1a; IL代码分析方法 Hello, world历史 .NET学习方法论1. 引言 1988年Brian W. Kernighan和Dennis M. Ritchie合著了软件史上的经典巨著《The C programming Language》&#xff0c;我推荐所有的程序人都有机会重温这本历史上的经典之作。从那时起…

[你必须知道的.NET]第十四回:认识IL代码---从开始到现在

本文将介绍以下内容&#xff1a; IL代码分析方法 IL命令解析 .NET学习方法论 1. 引言 自从『你必须知道.NET』系列开篇以来&#xff0c;受到大家很多的关注和支持&#xff0c;给予了anytao巨大的鼓励和动力。俱往昔&#xff0c;我发现很多的园友都把…