首页 > 作文

Quartz.Net使用方法详解

更新时间:2023-04-04 05:14:03 阅读: 评论:0

目录
hello quartz.net作业:job和jobdetailjobdatajobdetail持久化jobdata触发器:triggersampletriggercrontrigger日历:calendar监听器joblistenertriggerlistenerschedulerlistener持久化:jobstoreado.net存储负载均衡通过routing访问quartz实例开发实践参考资料

在项目的开发过程中,难免会遇见后需要后台处理的任务,例如定时发送邮件通知、后台处理耗时的数据处理等,这个时候你就需要quartz.net了。

quartz.net是纯净的,它是一个.net程序集,是非常流行的java作业调度系统quartz的c#实现。

quartz.net一款功能齐全的任务调度系统,从小型应用到大型企业级系统都能适用。功能齐全体现在触发器的多样性上面,即支持简单的定时器,也支持cron表达式;即能执行重复的作业任务,也支持指定例外的日历;任务也可以是多样性的,只要继承ijob接口即可。

对于小型应用,quartz.net可以集成到你的系统中,对于企业级系统,它提供了routing支持,提供了group来组织和管理任务,此外还有持久化、插件功能、负载均衡和故障迁移等满足不同应用场景的需要。

hello quartz.net

开始使用一个框架,和学习一门开发语言一样,最好是从hello world程序开始。

首先创建一个示例程序,然后添加quartz.net的引用。

install-package quartz -version 3.0.7

我们使用的是当前最新版本3.0.7进行演示。添加引用以后,来创建一个job类helloquartzjob

public class helloquartzjob : ijob{    public task execute(ijobexecutioncontext context)    {        return task.factory.startnew(() =>        {            console.writeline("hello quartz.net");        });    }}

这是个非常简单的job类,它在执行时输出文本hello quartz.net

接下来,我们在程序启动时创建调度器(scheduler),并添加helloquartzjob的调度:

static async task mainasync(){    var schedulerfactory = new stdschedulerfactory();    var scheduler = await schedulerfactory.getscheduler();    await scheduler.start();    console.writeline($"任务调度器已启动");    //创建作业和触发器    var jobdetail = jobbuilder.create<helloquartzjob>().build();    var trigger = triggerbuilder.create()                                .withsimpleschedule(m => {                                    m.withrepeatcount(3).withintervalinconds(1);                                })                                .build();    //添加调度    await scheduler.schedulejob(jobdetail, trigger);}

然后运行程序,你会看到如下图:

通过演示可以看出,要执行一个定时任务,一般需要四步:

创建任务调度器。调度器通常在应用程序启动时创建,一个应用程序实例通常只需要一个调度器即可。创建job和jobdetail。job是作业的类型,描述了作业是如何执行的,这个类是由我们定义的;jobdetail是quartz对作业的封装,它包含job类型,以及job在执行时用到的数据,还包括是否要持久化、是否覆盖已存在的作业等选项。创建触发器。触发器描述了在何时执行作业。添加调度。当完成以上三步以后,就可以对作业进行调度了。

作业:job和jobdetail

job是作业的类型,描述了作业是如何执行的,这个类型是由我们定义的,例如上文的hell屈臣氏橄榄油oquartzjob。job实现ijob接口,而ijob接口只有一个execute方法,参数context中包含了与当前上下文中关联的scheduler、jobdetail、trigger等。

一个典型的job定义如下:

public class helloquartzjob : ijob{    public task execute(ijobexecutioncontext context)    {        return task.factory.startnew(() =>        {            console.writeline("hello quartz.net");        });    }}

jobdata

job不是孤立存在的,它需要执行的参数,这些参数如何传递进来呢?我们来定义一个job类进行演示。

public class sayhellojob : ijob{    public string urname { get; t; }    public task execute(ijobexecutioncontext context)    {        return task.factory.startnew(() =>        {            console.writeline($"hello {urname}!");        });    }}

sayhellojob在执行时需要参数urname,这个参数被称为jobdata,quartz.net通过jobdatamap的方式传递参数。代码如下:

//创建作业var jobdetail = jobbuilder.create<sayhellojob>()                            .tjobdata(new jobdatamap() {                                new keyvaluepair<string, object>("urname", "tom")                            })                            .build();

通过jobbuilder的tjobdata方法,传入jobdatamap对象,jobdatamap对象中可以包含多个参数,这些参数可以映射到job类的属性上。我们完善代码运行示例,可以看到如下图:

jobdetail

jobdetail是quartz对作业的封装,它包含job类型,以及job在执行时用到的数据,还包括是否孤立存储、请求恢复作业等选项。

jobdetail是通过jobbuilder进行创建的。例如:

var jobdetail = jobbuilder.create<sayhellojob>()                            .tjobdata(new jobdatamap() {                                new keyvaluepair<string, object>("urname", "tom")                            })                            .storedurably(true)                            .requestrecovery(true)                            .withidentity("sayhellojob-tom", "demogroup")                            .withdescription("say hello to tom job")                            .build();

参数说明:

tjobdata:设置jobdatastoredurably:孤立存储,指即使该jobdetail没有关联的trigger,也会进行存储requestrecovery:请求恢复,指应用崩溃后再次启动,会重新执行该作业withidentity:作业的唯一标识withdescription:作业的描述信息

除此之外,quartz.net还支持两个非常有用的特性:

disallowconcurrentexecution:禁止并行执行,该特性是针对jobdetail生效的persistjobdataafterexecution:在执行完成后持久化jobdata,该特性是针对job类型生效的,意味着所有使用该job的jobdetail都会在执行完成后持久化jobdata。

持久化jobdata

我们来演示一下该persistjobdataafterexecution特性,在sayhellojob中,我们新加一个字段runsuccess,记录任务是否执行成功。

首先在sayhellojob添加特性:

[persistjobdataafterexecution]public class sayhellojob : ijob { }

然后在创建jobdetail时添加jobdata:

var jobdetail = jobbuilder.create<sayhellojob>()                            .tjobdata(new jobdatamap() {                                new keyvaluepair<string, object>("urname", "tom"),                                new keyvaluepair<string, object>("runsuccess", fal)                            })

在执行时job时,更新runsuccess的值:

public task execute(ijobexecutioncontext context){    return task.factory.startnew(() =>    {        console.writeline($"prev run success:{runsuccess}");        console.writeline($"hello {urname}!");        context.jobdetail.jobdatamap.put("runsuccess", true);    });}

接下来看一下执行效果:

触发器:trigger

trigger是触发器,用来定制执行作业。trigger有两种类型:sampletrigger和crontrigger,我们分别进行说明。

sampletrigger

顾名思义,这是个简单的触发器,有以下特性:

重复执行:withrepeatcount()/repeatforever()设置间隔时间:withinterval()定时执行:startat()/startnow()设定优先级:withpriority(),默认为5

需要注意:当trigger到达startat指定的时间时会执行一次,这一次执行是不包含在withrepeatcount中的。在我们上面的例子中可以看出,添加调度后会立即执行一次,然后重复三次,最终执行了四次。

crontrigger

crontrigger是通过cron表达式来完成调度的。cron表达式非常灵活,可以实现几乎各种定时场景的需要。

关于cron表达式,大家可以移步 quartz cron表达式

使用crontrigger的示例如下:

var trigger = triggerbuilder.create()                            .withcronschedule("*/1 * * * * ?")                            .build();

日历:calendar

calendar可以与trigger进行关联,从trigger中排出执行计划。例如你只希望在工作日执行作业,那么我们可以定义一个休息日的日历,将它与trigger关联,从而排出休息日的执行计划。

calendar示例代码如下:

var calandar = new holidaycalendar();calandar.addexcludeddate(datetime.today);await scheduler.addcalendar("holidaycalendar", calandar, fal, fal);var trigger = triggerbuilder.create()                        .withcronschedule("*/1 * * * * ?")                        .modifiedbycalendar("holidaycalendar")                        .build();

在这个示例中,我们创建了holidaycalendar日历,然后添加排除执行的日期。我们把今天添加到排除日期后,该trigger今天将不会触发。

监听器

joblistenerstriggerlistenersschedulerlisteners

监听器是quartz.net的另外一个出色的功能,它允许我们编写监听器达到在运行时获取作业状态、处理作业数据等功能。

joblistener

joblistener可以监听job执行前、执行后、否决执行的事件。我们通过代码进行演示:

public class myjoblistener : ijoblistener{    public string name { get; } = nameof(myjoblistener);    public task jobtobeexecuted(ijobexecutioncontext context, cancellationtoken cancellationtoken = default)    {        //job即将执行        return task.factory.startnew(() =>        {            console.writeline($"job: {context.jobdetail.key} 即将执行");        });    }    public task jobexecutionvetoed(ijobexecutioncontext context, cancellationtoken cancellationtoken = default)    {        return task.factory.startnew(()=> {            console.writeline($"job: {context.jobdetail.key} 被否决执行");        });    }    public task jobwaxecuted(ijobexecutioncontext context, jobexecutionexception jobexception, cancellationtoken cancellationtoken = default)    {        //job执行完成        return task.factory.startnew(() =>        {            console.writeline($"job: {context.jobdetail.key} 执行完成");        });    }}

定义完成后,将myjoblistener添加到scheduler中:

scheduler.listenermanager.addjoblistener(new myjoblistener(), groupmatcher<jobkey>.anygroup());

然后我们再运行程序,就可以看到listener被调用了:

通过图片可以看到,jobtobeexecutedjobwaxecuted都被执行了,jobexecutionvetoed没有执行,那么如何触发jobexecutionvetoed呢?请继续阅读triggerlistener的演示。

triggerlistener

triggerlistener可以监听trigger的执行情况,我们通过代码进行演示:

public class mytriggerlistener : itriggerlistener{    public string name { get; } = nameof(mytriggerlistener);    public task triggercomplete(itrigger trigger, ijobexecutioncontext context, schedulerinstruction triggerinstructioncode, cancellationtoken cancellationtoken = default)    {        return task.completedtask;    }    public task triggerfired(itrigger trigger, ijobexecutioncontext context, cancellationtoken cancellationtoken = default)    {        return task.completedtask;    }    public task triggermisfired(itrigger trigger, cancellationtoken cancellationtoken = default)    {        return task.completedtask;    }    publ踔厉风发ic task<bool> vetojobexecution(itrigger trigger, ijobexecutioncontext context, cancellationtoken cancellationtoken = default)    {        return task.fromresult(true);   //返回true表示否决job继续执行    }}

mytriggerlistener添加到scheduler中:

scheduler.listenermanager.addtriggerlistener(new mytriggerlistener(), groupmatcher<triggerkey>.anygroup());

运行代码可以看到如下效果:

从图片中可以看到,joblistener中的jobexecutionvetoed被执行了。

schedulerlistener

ischedulerlistener提供了job、trigger管理的监听,与调度程序相关的事件包括:添加作业/触发器,删除作业/触发器,调度程序中的严重错误,调度程序关闭的通知等。完整的接口定义如下:

public interface ischedulerlistener{    task jobadded(ijobdetail jobdetail, cancellationtoken cancellationtoken = default);    task jobdeleted(jobkey jobkey, cancellationtoken cancellationtoken = default);    task jobinterrupted(jobkey jobkey, cancellationtoken cancellationtoken = default);    task jobpaud(jobkey jobkey, cancellationtoken cancellationtoken = default);    task jobresumed(jobkey jobkey, cancellationtoken cancellationtoken = default);    task jobscheduled(itrigger trigger, cancellationtoken cancellationtoken = default);    task jobspaud(string jobgroup, cancellationtoken cancellationtoken = default);    task jobsresumed(string jobgroup, cancellationtoken cancellationtoken = default);    task jobunscheduled(triggerkey triggerkey, cancellationtoken cancellationtoken = default);    task schedulererror(string msg, schedulerexception cau, cancellationtoken cancellationtoken = default);    task schedulerinstandbymode(cancellationtoken cancellationtoken = default);    task schedulershutdown(cancellationtoken cancellationtoken = default);    task schedulershuttingdown(cancellationtoken cancellationtoken = default);    task schedulerstarted(cancellationtoken cancellationtoken = default);    task schedulerstarting(cancellationtoken cancellationtoken = default);    task schedulingdatacleared(cancellationtoken cancellationtoken = default);    task triggerfinalized(itrigger trigger, cancellationtoken cancellationtoken = default);    task triggerpaud(triggerkey triggerkey, cancellationtoken cancellationtoken = default);    task triggerresumed(triggerkey triggerkey, cancellationtoken cancellationtoken = default);    task triggerspaud(string triggergroup, cancellationtoken cancellationtoken = default);   什么是古体诗 task triggersresumed(string triggergroup, cancellationtoken cancellationtoken = default);}

添加schedulerlistener的代码如下:

scheduler.listenermanager.addschedulerlistener(myschedlistener);

持久化:jobstore

quartz.net支持job的持久化操作,被称为jobstore。默认情况下,quartz将数据持久化到内存中,好处是内存的速度很快,坏处是无法提供负载均衡的支持,并且在程序崩溃后,我们将丢失所有job数据,对于企业级系统来说,坏处明显大于好处,因此有必要将数据存储在数据库中。

ado.net存储

quartz使用ado.net访问数据库,支持的数据库厂商非常广泛:

sqlrver – .net framework 2.0的sql rver驱动程序oracleodp – oracle的oracle驱动程序oracleodpmanaged – oracle的oracle 11托管驱动程序mysql – mysql connector / .netsqlite – sqlite ado.net providersqlite-microsoft – microsoft sqlite ado.net providerfirebird – firebird ado.net提供程序npgsql – postgresql npgsql

数据库的创建语句可以在quartz.net的源码中找到:https://github.com/quartznet/quartznet/tree/master/databa/tables

我们可以通过配置文件来配置quartz使用数据库存储:

# job storequartz.jobstore.type = quartz.impl.adojobstore.jobstoretx, quartzquartz.jobstore.datasource = quartz_storequartz.jobstore.driverdelegatetype = quartz.impl.adojobstore.postgresqldelegate, quartz#quartz.jobstore.uproperties = truequartz.datasource.quartz_store.connectionstring = rver=localhost;databa=quartz_store;urid=quartz_net;password=xxxxxx;pooling=true;minpoolsize=1;maxpoolsize=10;timeout=15;sslmode=disable;quartz.datasource.quartz_store.provider = npgsql

负载均衡

负载均衡是实现高可用的一种方式,当任务量变大以后,单台服务器很难满足需要,使用负载均衡则使得系统具备了横向扩展的能力,通过部署多个节点来增加处理job的能力。

quartz.net在使用负载均衡时,需要依赖ado jobstore,意味着你需要使用数据库持久化数据。然后我们可以使用以下配置完成负载均衡功能:

quartz.jobstore.clustered = truequartz.scheduler.instanceid = auto
clustered:集群的标识instanceid:当前scheduler实例的id,每个示例的id不能重复,使用auto时系统会自动生成id

当我们在多台服务器上运行scheduler实例时,需要设置服务器的时钟时间,确保服务器时间是相同的。针对windows服务器,可以设置从网络自动同步时疯狂猜图啤酒间。

通过routing访问quartz实例

通过routing访问quartz实例的功能,为我们做系统分离提供了很好的途径。

我们可以通过以下配置实现quartz的服务器端远程访问:

# export this rver to remoting contextquartz.scheduler.exporter.type = quartz.simpl.remotingschedulerexporter, quartzquartz.scheduler.exporter.port = 555quartz.scheduler.exporter.bindname = quartzschedulerquartz.scheduler.exporter.channeltype = tcpquartz.scheduler.exporter.channelname = httpquartz

然后我们在客户端系统中配置访问:

quartz.scheduler.proxy = truequartz.scheduler.proxy.address = tcp://localhost:555/quartzscheduler

开发实践

理想中的任务调度系统应该是一个后台服务,默无声息的运行在系统后台,业务系统通过接口完成对任务的添加、删除等操作。在构架windows服公路绿化务时,可以和topshelf集成完成windows服务的开发。

install-package topshelf

进行服务开发的另外一个问题是,quartz本身是不支持依赖注入的,而解决依赖注入的问题,则可以使用autofac,幸运的是已经有大神完成了topshelf与autofac的集成,我们只需要使用即可。

install-package topshelf.autofac

quartz.net job的添加有两种方式:运行时动态添加和通过配置文件添加。这里推荐使用动态的方式进行添加(示例代码是采用动态方式进行添加的),除非你的job是相对固定的。

而对scheduler的配置可以采用配置文件的方式,方便在部署时进行维护。

参考资料

quartz.net官方文档

github:quartz.net源码

quartz cron表达式

samplequartz源码下载

到此这篇关于quartz.net使用方法详解的文章就介绍到这了。希望对大家的学习有所帮助,也希望大家多多支持www.887551.com。

本文发布于:2023-04-04 05:13:28,感谢您对本站的认可!

本文链接:https://www.wtabcd.cn/fanwen/zuowen/8c66d165bbcee5c74724ddabda02cb86.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

本文word下载地址:Quartz.Net使用方法详解.doc

本文 PDF 下载地址:Quartz.Net使用方法详解.pdf

标签:作业   触发器   持久   系统
相关文章
留言与评论(共有 0 条评论)
   
验证码:
Copyright ©2019-2022 Comsenz Inc.Powered by © 专利检索| 网站地图