asp.net Quartz.NET调度程序在部署后不会触发作业/触发器

sxpgvts3  于 2023-02-10  发布在  .NET
关注(0)|答案(4)|浏览(412)

引言

我在 *ASP.NET * framework 4的webforms网站上使用 *Quartz .NET *。基本上,用户应该能够手动启动一个批处理脚本,异步处理存储在数据库中的数千条记录。用户可以随时停止或暂停,调整一些变量,如果需要,继续处理(剩余记录)。
代码已完成,并在本地运行(开发人员计算机、win7、vs2010、sql server express 2008 R2)。
还在本地服务器上进行了测试(win server 2008 R2,sql server express 2008 R2)。它在两种环境下都能正常工作,测试时所有代码都是预编译的。问题是,一旦部署到远程服务器上(win server 2008 R2),它实际上应该运行在(托管环境,非共享,非集群),它不完全工作(见下面的详细信息)。调度程序创建,但触发器,因此作业,不激发
(**注:**我知道你们中的一些人会建议使用Quartz作为Windows服务,但尽管这样做有好处,我还是想知道为什么它不能作为嵌入式解决方案工作,因为它应该像本地一样工作得很好)

详细信息

Quartz 2.1.2  
Common.Logging 2.1.2  
Common.Logging.NLog 2.0.0  
NLog 2.0.1.2

全局.asax

public static ISchedulerFactory SchedulerFactory;
public static IScheduler Scheduler;

void Application_Start(object sender, EventArgs e)
{
    SchedulerFactory = new StdSchedulerFactory();
    Scheduler = SchedulerFactory.GetScheduler();

    // Define a durable job instance (durable jobs can exist without triggers)
    IJobDetail job = JobBuilder.Create<MyJobClass>()
                                .WithIdentity("MyJob", "MyGroup")
                                .StoreDurably()
                                .Build();

    Scheduler.AddJob(job, false);
    Scheduler.Start();
}
void Application_End(object sender, EventArgs e)
{
    Scheduler.Shutdown(true);
}

process.aspx.cs(单击“开始”按钮)

// get records from DB, iterate, process, etc
...

IJobDetail job = ASP.global_asax.Scheduler.GetJobDetail(new JobKey("MyJob", "MyGroup"));
job.JobDataMap.Put("something1", 1);
job.JobDataMap.Put("something2", somevar);

ITrigger trigger = TriggerBuilder.Create()
                    .WithIdentity("MyTrigger", "MyGroup")
                    .StartNow()
                    .WithSimpleSchedule(x => x.WithIntervalInSeconds(5).RepeatForever())
                    .Build();

var triggersSet = new Quartz.Collection.HashSet<ITrigger> { trigger };

ASP.global_asax.Scheduler.ScheduleJob(job, triggersSet, true);

日志输出

本地日志

Default Quartz.NET properties loaded from embedded resource file  
Using default implementation for object serializer  
Using default implementation for ThreadExecutor  
Initialized Scheduler Signaller of type: Quartz.Core.SchedulerSignalerImpl  
Quartz Scheduler v.2.1.2.400 created.  
RAMJobStore initialized.  
Scheduler meta-data: Quartz Scheduler (v2.1.2.400) 'DefaultQuartzScheduler' with instanceId 'NON_CLUSTERED'   Scheduler class: 'Quartz.Core.QuartzScheduler' - running locally.   NOT STARTED.   Currently in standby mode.   Number of jobs executed: 0   Using thread pool 'Quartz.Simpl.SimpleThreadPool' - with 10 threads.   Using job-store 'Quartz.Simpl.RAMJobStore' - which does not support persistence. and is not clustered.  
Quartz scheduler 'DefaultQuartzScheduler' initialized  
Quartz scheduler version: 2.1.2.400  
Scheduler DefaultQuartzScheduler_$_NON_CLUSTERED started.  
Batch acquisition of 0 triggers  
Batch acquisition of 0 triggers

它继续记录 * 0个触发的批量采集 *,直到点击按钮:

Default Quartz.NET properties loaded from embedded resource file  
Batch acquisition of 1 triggers  
Producing instance of Job 'MyGroup.MyJob', class=MyJobClass  
Batch acquisition of 0 triggers  
Calling Execute on job MyGroup.MyJob  
Trigger instruction : NoInstruction  
Batch acquisition of 1 triggers  
Producing instance of Job 'MyGroup.MyJob', class=MyJobClass  
Batch acquisition of 0 triggers  
Calling Execute on job MyGroup.MyJob  
Trigger instruction : NoInstruction  
Batch acquisition of 1 triggers

部署日志

Default Quartz.NET properties loaded from embedded resource file  
Using default implementation for object serializer  
Using default implementation for ThreadExecutor  
Initialized Scheduler Signaller of type: Quartz.Core.SchedulerSignalerImpl  
Quartz Scheduler v.2.1.2.400 created.  
RAMJobStore initialized.  
Scheduler meta-data: Quartz Scheduler (v2.1.2.400) 'DefaultQuartzScheduler' with instanceId 'NON_CLUSTERED' Scheduler class: 'Quartz.Core.QuartzScheduler' - running locally. NOT STARTED. Currently in standby mode. Number of jobs executed: 0 Using thread pool 'Quartz.Simpl.SimpleThreadPool' - with 10 threads. Using job-store 'Quartz.Simpl.RAMJobStore' - which does not support persistence. and is not clustered.   
Quartz scheduler 'DefaultQuartzScheduler' initialized  
Quartz scheduler version: 2.1.2.400  
Scheduler DefaultQuartzScheduler_$_NON_CLUSTERED started.

这里是这样的。正如你所看到的,与其他日志相比,它没有尝试获取触发器(行 * 0触发器的批量获取 * 根本没有出现)。如果你还是点击了进程按钮,日志会添加一行:

Default Quartz.NET properties loaded from embedded resource file

但不会发生其他任何事情。记录不会被处理(我知道,因为每次处理记录时,都会在数据库中标记)。没有错误发生,但触发器没有触发,作业也没有执行。此外,CPU使用率在按钮点击时高达50%或更多,除非你转到IIS,否则不会下降,停止并重新启动应用程序池。此CPU消耗不会在本地发生。

更新1

按照LeftyX的建议,更改了单例的调度程序使用,但在远程服务器上仍获得相同的行为。

更新2

我还尝试使用ADOJobStore(而不是我正在使用的 RAMJobStore),现在它仍然可以在本地完美地工作;但仍然无法在线执行触发器(因此无法在线执行作业)。唯一的区别是,在线CPU使用率不会达到50%。现在我可以看到作业和触发器已创建(我查询了表,发现这些记录存在),但从未执行过

dfty9e19

dfty9e191#

Quartz没有任何问题,这都是因为IIS应用程序池回收。我通过停止回收Quartz使用的池来修复此错误:
1.转到IIS管理器-〉应用程序池-〉创建一个新池,我将其命名为Scheduler(任何名称都可以)
1.选择计划程序池-〉高级设置

  • 常规部分的启动模式下,选择始终运行(IIS 8.5)或(IIS 7.5、8)
  • Process Model部分-〉Idle Timeout(minutes)中设置为0(表示:无Idel超时)
  • 回收部分-〉常规时间间隔中设置为0(表示:无回收)

3.将你的Quartz站点部署到这个应用池中。然后向池中发送一个"唤醒你的应用"的请求,它会一直运行直到你停止它。

就是这样。

aiazj4mn

aiazj4mn2#

我注意到的一件事是在您的www.example.com应用程序中使用了Scheduler。asp.net application.
应该使用单例对象。
在您process.aspx.cs此行中

IScheduler scheduler = new StdSchedulerFactory().GetScheduler();

创建一个新的调度程序,但您应该使用在Application_Start中创建的静态调度程序。
如果你想访问singleton示例,请在Global.asax.cs中使用一个公共成员:

public static ISchedulerFactory SchedulerFactory;
 public static IScheduler Scheduler;

你可以在process.aspx.cs中引用它:

MvcApplication.Scheduler.ScheduleJob(job, triggersSet, true);

另一个解决方案是使用依赖注入,你可以使用StructureMap找到一些here的信息,使用Unity找到一些here的信息。
更新:
您可以下载一个名为AspNet_Quartzhere的示例应用程序(asp.net 4.0),并查看它是如何工作的。

af7jpaap

af7jpaap3#

问题与IIS有关,而与调度器Quartz.NETHangfire等无关,另一方面,网络上发布了很多解决方法,但只有一部分起作用,在我看来,不需要应用大量的配置设置,只需在发布应用的服务器上安装Keep Alive Service For IIS 6.0/7.5并享受即可,之后,在应用程序池回收、IIS/应用程序重新启动等操作之后,您发布的应用程序仍将处于活动状态。

btqmn9zl

btqmn9zl4#

我刚刚遇到了一个类似的问题,可能会咬别人-它把我带到这个如此的问题后,打开调试和获得的“批采集0触发器”的消息。
我的工作是每两个小时这样的:

"0 0 0/2 * * ?"

我想多做几次,所以每两分钟就像这样:

"0 0/2 0 * ?"

我甚至尝试了https://cronexpressiondescriptor.azurewebsites.net/,这给了我一个很大的线索,我应该更仔细地阅读:每2小时,在一个月的第0天这最终让我意识到我真正的意思是:

"0 0/2 * * ?"

因此,教训是,当“左移”你的cron,回填 *,而不是0。
希望这对其他人有帮助。

相关问题