双机热备的Quartz集群

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

  一般拿Timer和Quartz相比较的,简直就是对Quartz的侮辱,两者的功能根本就不在一个层级上,如本篇介绍的Quartz强大的集群机制,可以采用基于

sqlserver,mysql的集群方案,当然还可以在第三方插件的基础上实现quartz序列化到热炒的mongodb,redis,震撼力可想而知,接下来本篇就和大家聊

一聊怎么搭建基于sqlserver的quartz集群,实现这么一种双机热备的强大功能。

一:下载sqlserver版的建表脚本

    首先大家可以通过github上搜索quartz的源代码,在源码项目的/database/tables目录下,可以找到firebird,oracle,mysql,sqlserver等建库脚本,

本篇只需拿取sqlserver版本即可。 https://github.com/quartznet/quartznet/tree/master/database/tables  如下图所示

从上面的截图中可以看到,我接下来要做的事情就是增加一个你需要创建的database名字,这里取为:【quartz】,完整的脚本如下:

 

二:配置quartz的集群参数

当我们写var scheduler = StdSchedulerFactory.GetDefaultScheduler()这段代码的时候,如果大家看过源码的话,会知道这个GetScheduler的

过程中有一个初始化方法【Instantiate】方法,此方法中你会发现在做DBProvider的时候会需要几个参数来初始化DB的,比如下面看到的几个标红属性。

 1             IList<string> dsNames = cfg.GetPropertyGroups(PropertyDataSourcePrefix);
 2             foreach (string dataSourceName in dsNames)
 3             {
 4                 string datasourceKey = "{0}.{1}".FormatInvariant(PropertyDataSourcePrefix, dataSourceName);
 5                 NameValueCollection propertyGroup = cfg.GetPropertyGroup(datasourceKey, true);
 6                 PropertiesParser pp = new PropertiesParser(propertyGroup);
 7
 8                 Type cpType = loadHelper.LoadType(pp.GetStringProperty(PropertyDbProviderType, null));
 9
10                 // custom connectionProvider...
11                 if (cpType != null)
12                 {
13                     IDbProvider cp;
14                     try
15                     {
16                         cp = ObjectUtils.InstantiateType<IDbProvider>(cpType);
17                     }
18                     catch (Exception e)
19                     {
20                         initException = new SchedulerException("ConnectionProvider of type ‘{0}‘ could not be instantiated.".FormatInvariant(cpType), e);
21                         throw initException;
22                     }
23
24                     try
25                     {
26                         // remove the type name, so it isn‘t attempted to be set
27                         pp.UnderlyingProperties.Remove(PropertyDbProviderType);
28
29                         ObjectUtils.SetObjectProperties(cp, pp.UnderlyingProperties);
30                         cp.Initialize();
31                     }
32                     catch (Exception e)
33                     {
34                         initException = new SchedulerException("ConnectionProvider type ‘{0}‘ props could not be configured.".FormatInvariant(cpType), e);
35                         throw initException;
36                     }
37
38                     dbMgr = DBConnectionManager.Instance;
39                     dbMgr.AddConnectionProvider(dataSourceName, cp);
40                 }
41                 else
42                 {
43                     string dsProvider = pp.GetStringProperty(PropertyDataSourceProvider, null);
44                     string dsConnectionString = pp.GetStringProperty(PropertyDataSourceConnectionString, null);
45                     string dsConnectionStringName = pp.GetStringProperty(PropertyDataSourceConnectionStringName, null);
46
47                     if (dsConnectionString == null && !String.IsNullOrEmpty(dsConnectionStringName))
48                     {
49
50                         ConnectionStringSettings connectionStringSettings = ConfigurationManager.ConnectionStrings[dsConnectionStringName];
51                         if (connectionStringSettings == null)
52                         {
53                             initException = new SchedulerException("Named connection string ‘{0}‘ not found for DataSource: {1}".FormatInvariant(dsConnectionStringName, dataSourceName));
54                             throw initException;
55                         }
56                         dsConnectionString = connectionStringSettings.ConnectionString;
57                     }
58
59                     if (dsProvider == null)
60                     {
61                         initException = new SchedulerException("Provider not specified for DataSource: {0}".FormatInvariant(dataSourceName));
62                         throw initException;
63                     }
64                     if (dsConnectionString == null)
65                     {
66                         initException = new SchedulerException("Connection string not specified for DataSource: {0}".FormatInvariant(dataSourceName));
67                         throw initException;
68                     }
69                     try
70                     {
71                         DbProvider dbp = new DbProvider(dsProvider, dsConnectionString);
72                         dbp.Initialize();
73
74                         dbMgr = DBConnectionManager.Instance;
75                         dbMgr.AddConnectionProvider(dataSourceName, dbp);
76                     }
77                     catch (Exception exception)
78                     {
79                         initException = new SchedulerException("Could not Initialize DataSource: {0}".FormatInvariant(dataSourceName), exception);
80                         throw initException;
81                     }
82                 }
83             }

接下来的问题就是这几个属性是如何配置进去的,仔细观察上面代码,你会发现所有的配置的源头都来自于cfg变量,ok,接下来你可以继续翻看代码,相信

你会看到有一个Initialize方法就是做cfg变量的初始化,如下代码所示:

 1   public void Initialize()
 2         {
 3             // short-circuit if already initialized
 4             if (cfg != null)
 5             {
 6                 return;
 7             }
 8             if (initException != null)
 9             {
10                 throw initException;
11             }
12
13             NameValueCollection props = (NameValueCollection) ConfigurationManager.GetSection(ConfigurationSectionName);
14
15             string requestedFile = QuartzEnvironment.GetEnvironmentVariable(PropertiesFile);
16
17             string propFileName = requestedFile != null && requestedFile.Trim().Length > 0 ? requestedFile : "~/quartz.config";
18
19             // check for specials
20             try
21             {
22                 propFileName = FileUtil.ResolveFile(propFileName);
23             }
24             catch (SecurityException)
25             {
26                 log.WarnFormat("Unable to resolve file path ‘{0}‘ due to security exception, probably running under medium trust");
27                 propFileName = "quartz.config";
28             }
29
30             if (props == null && File.Exists(propFileName))
31             {
32                 // file system
33                 try
34                 {
35                     PropertiesParser pp = PropertiesParser.ReadFromFileResource(propFileName);
36                     props = pp.UnderlyingProperties;
37                     Log.Info(string.Format("Quartz.NET properties loaded from configuration file ‘{0}‘", propFileName));
38                 }
39                 catch (Exception ex)
40                 {
41                     Log.Error("Could not load properties for Quartz from file {0}: {1}".FormatInvariant(propFileName, ex.Message), ex);
42                 }
43
44             }
45             if (props == null)
46             {
47                 // read from assembly
48                 try
49                 {
50                     PropertiesParser pp = PropertiesParser.ReadFromEmbeddedAssemblyResource("Quartz.quartz.config");
51                     props = pp.UnderlyingProperties;
52                     Log.Info("Default Quartz.NET properties loaded from embedded resource file");
53                 }
54                 catch (Exception ex)
55                 {
56                     Log.Error("Could not load default properties for Quartz from Quartz assembly: {0}".FormatInvariant(ex.Message), ex);
57                 }
58             }
59             if (props == null)
60             {
61                 throw new SchedulerConfigException(
62                     @"Could not find <quartz> configuration section from your application config or load default configuration from assembly.
63 Please add configuration to your application config file to correctly initialize Quartz.");
64             }
65             Initialize(OverrideWithSysProps(props));
66         }

仔细阅读上面的一串代码,你会发现,默认quartz参数配置来源于三个地方。

1. app.config中的section节点。

2. bin目录下的~/quartz.config文件。

3. 默认配置的NameValueCollection字典集合,也就是上一篇博客给大家做的一个演示。

我个人不怎么喜欢通过quartz.config文件进行配置,这样也容易写死,所以我还是喜欢使用最简单的NameValueCollection配置,因为它的数据源可来源

于第三方存储结构中,配置代码如下:

 1                 //1.首先创建一个作业调度池
 2                 var properties = new NameValueCollection();
 3                 //存储类型
 4                 properties["quartz.jobStore.type"] = "Quartz.Impl.AdoJobStore.JobStoreTX, Quartz";
 5
 6                 //驱动类型
 7                 properties["quartz.jobStore.driverDelegateType"] = "Quartz.Impl.AdoJobStore.SqlServerDelegate, Quartz";                //数据源名称
 8                 properties["quartz.jobStore.dataSource"] = "myDS";
 9
10                 //连接字符串
11                 properties["quartz.dataSource.myDS.connectionString"] = @"server=.;Initial Catalog=quartz;Integrated Security=True";
12                 //sqlserver版本
13                 properties["quartz.dataSource.myDS.provider"] = "SqlServer-20";
14
15                 //是否集群
16                 properties["quartz.jobStore.clustered"] = "true";
17                 properties["quartz.scheduler.instanceId"] = "AUTO";

上面的代码配置我都加过详细的注释,大家应该都能看得懂,而且这些配置就是这么定死的,没什么修改的空间,大家记住即可。

三:Job和Trigger定义

在集群中环境下,job和trigger的定义该怎么写的?大家也不要想的太复杂,注意一点就可以了,在Schedule一个Job时候,通过CheckExists判断一下

这个Job在Scheduler中是否已经存在了,如果存在,你就不能再次通过Schedule去重复调度一个Job就可以了。。。所以判断的代码也很简单,如下所示:

 1          IScheduler scheduler = factory.GetScheduler();
 2
 3                 scheduler.Start();
 4
 5                 var jobKey = JobKey.Create("myjob", "group");
 6
 7                 if (scheduler.CheckExists(jobKey))
 8                 {
 9                     Console.WriteLine("当前job已经存在,无需调度:{0}", jobKey.ToString());
10                 }
11                 else
12                 {
13                     IJobDetail job = JobBuilder.Create<HelloJob>()
14                            .WithDescription("使用quartz进行持久化存储")
15                            .StoreDurably()
16                            .RequestRecovery()
17                            .WithIdentity(jobKey)
18                            .UsingJobData("count", 1)
19                            .Build();
20
21                     ITrigger trigger = TriggerBuilder.Create().WithSimpleSchedule(x => x.WithIntervalInSeconds(2).RepeatForever())
22                                                               .Build();
23
24                     scheduler.ScheduleJob(job, trigger);
25
26                     Console.WriteLine("调度进行中!!!");
27                 }

上面这段代码,大家就可以部署在多台机器中了,是不是很简单?

四:强大的cluster完整演示

所有的初始化工作都做完了,接下来我们copy一份bin文件,同时打开两个console程序,如下所示,可以看到job任务只会被一个console调度,另外

一个在空等待。

然后你肯定很好奇的跑到sqlserver中去看看,是否已经有job和trigger的db存储,很开心吧,数据都有的。。。

好了,一切都是那么完美,接下来可以展示一下quartz集群下的高可用啦,如果某一个console挂了,那么另一台console会把这个任务给接过来,实

现强大的高可用。。。所以我要做的事情就是把console1关掉,再看看console2是不是可以开始调度job了???

完美,这个就是本篇给大家介绍的Quartz的Cluster集群,一台挂,另一台顶住,双机热备,当然这些console你可以部署在多台机器中,要做的就是保持各

个server的时间同步,因为quarz是依赖于本机server的时间,好了,本篇就先说到这里吧。

小礼物走一波,双击666。。。  完整代码:SimpleSchedulerApp.zip

时间: 2024-10-12 11:31:01

双机热备的Quartz集群的相关文章

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

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

HA高可用集群部署(ricci+luci+fence) 双机热备

主机环境 redhat6.5 6位 实验环境 服务端1 ip172.25.29.1   主机名:server1.example.com   ricci     服务端2 ip172.25.29.2    主机名:server2.example.com    ricci     管理端1 ip172.25.29.3    主机名:server3.example.com    luci     管理端2 ip172.25.29.250     fence_virtd 防火墙状态:关闭 1. 安装ri

keepalived+LVS 实现双机热备、负载均衡、失效转移 高性能 高可用 高伸缩性 服务器集群

本章笔者亲自动手,使用LVS技术实现实现一个可以支持庞大访问量.高可用性.高伸缩性的服务器集群 在读本章之前,可能有不少读者尚未使用该技术,或者部分读者使用Nginx实现应用层的负载均衡.这里大家都可以阅读本章,即使部分读者使用Nginx负载均衡,但是在大流量下性能相对于工作在链路层的LVS真是不能同日而语,并且LVS不仅可以实现WEB方面的负载均衡,其他诸如数据库.FTP.Mail等都可以实现. 通常对于小型网站,很多都使用单台服务器,顶多在弄个缓存服务器.数据库服务器.但是一旦流量上来,单台

HA-集群(High available)高可用性集群(双机热备)菜鸟入门级

HA(High available)高可用性集群(双机热备)   1.理解:两台服务器A和B ,当A提供服务,B闲置待命,当A服务宕机,会自动切换至B机器继续提供服务.当主机恢复正常后,按照使用者设定的自动或手动切换到主机上运行,数据一致性通过共享存储系统解决. 2.实现该功能的软件有:Heartbeat , keepalived(具有负载均衡的能力) 3.结构图 HA-Heartbeat 实验:以hearbeat为例,来做HA集群,并把nginx服务作为HA对应的服务 关闭防火墙:iptabl

MSCS双机热备集群截图

MSCS双机热备集群 DNS记录截图 其中一节点的MSCS截图

基于RHCS的web双机热备集群搭建

基于RHCS的web双机热备集群搭建 RHCS集群运行原理及功能介绍 1. 分布式集群管理器(CMAN) Cluster Manager,简称CMAN,是一个分布式集群管理工具,它运行在集群的各个节点上,为RHCS提供集群管理任务.CMAN用于管理集群成员.消息和通知.它通过监控每个节点的运行状态来了解节点成员之间的关系,当集群中某个节点出现故障,节点成员关系将发生改变,CMAN及时将这种改变通知底层,进而做出相应的调整. 2.锁管理(DLM) Distributed Lock Manager,

NEC高可用集群软件NEC EXPRESSCLUSTER是一款专业的高可用集群软件产品(双机热备软件)

NEC高可用集群软件NEC EXPRESSCLUSTER是一款专业的高可用集群软件产品(双机热备软件)商务qq1912078946 ,可为您提供Windows和Linux平台上完整的高可用性解决方案.当集群中的某个节点由于软件或硬件原因发生故障时,集群系统可以把IP.客户业务等资源切换到其他健康的节点上,使整个系统能连续不间断的对外提供服务,并且可以通过对系统资源的使用情况进行分析来预防故障,自动判断出最适合运行业务的服务器,并进行切换,从而为机构24x365的关键业务提供了可靠的保障,达到了系

mysql双机热备+heartbeat集群+自动故障转移

环境说明:本环境由两台mysql 数据库和heartbeat 组成,一台的ip 为 192.168.10.197,一台为192.168.10.198,对外提供服务的vip 为192.168.10.200 备注:heartbeat 本身是不能做到服务不可用自动切换的,所以用结合 额外的脚本才可以做到,本文中提到的moniter 脚本即为实现某个 mysql 服务不可用的时候自动切换的还可以自动报警 安装和配置过程分为如下几步: 第一部分:mysql 的安装配置 1 安装 1.1. 添加mysql

用haproxy搭建web群集并由keepalived 实现双机热备

搭建haproxy+keepalived高可用群集. 一.案例概述 1.haproxy是目前比较流行的一种集群调度工具,是一款免费开源的软件,并且具有一定得安全性:haproxy较适用于负载较大的web节点,并且支持数万的并发量.同类调度工具还有很多,如LVS和Nginx.相比较而言,LVS性能最好,但是搭建相对复杂,Nginx的upstream模块支持群集功能,但是对群集节点健康检查功能不强,性能没有haproxy好.haproxy主要实现web群集的负载均衡haproxy官方网站:http: