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

zed*_*zed 9 asp.net quartz.net

介绍

我使用Quartz.Net上的ASP.Net框架4,WebForms的网站.基本上,用户应该无法手动触发批处理脚本,该脚本异步处理存储在数据库中的数千条记录.用户可以随时停止或暂停,调整一些变量,并在需要时继续处理(剩余记录).
代码在本地完成并运行(开发人员机器,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
Run Code Online (Sandbox Code Playgroud)

Global.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);
}
Run Code Online (Sandbox Code Playgroud)

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);
Run Code Online (Sandbox Code Playgroud)

日志输出

本地日志

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
Run Code Online (Sandbox Code Playgroud)

它继续记录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
Run Code Online (Sandbox Code Playgroud)

部署日志

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.
Run Code Online (Sandbox Code Playgroud)

在这里保持这样.如您所见,与其他日志相比,它并没有尝试获取触发器(线路批量获取0触发器根本没有出现).如果您仍然单击进程按钮,则日志会添加一行:

Default Quartz.NET properties loaded from embedded resource file
Run Code Online (Sandbox Code Playgroud)

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


更新1

根据LeftyX的建议,更改了对单例的调度程序的使用,但仍然在远程服务器上获得相同的行为.

更新2

我也尝试使用ADOJobStore(而不是我正在使用的RAMJobStore).现在它仍然在当地完美运作; 但仍然没有在线执行触发器(因此工作).唯一的区别是在线CPU使用率不高达50%.现在我可以看到创建了作业和触发器(我查询表并查看这些记录是否存在),但永远不会被执行.

Chr*_*han 6

Quartz 没有任何问题,都是因为 IIS 应用程序池回收。我通过停止循环使用 Quartz 的池来修复该错误:

  1. 转到IIS 管理器->应用程序池-> 创建一个新池,我将其命名为 Scheduler(任何名称都可以)
  2. 选择调度程序池->高级设置
  • 常规部分的启动模式中,选择AlwaysRunning (IIS 8.5) 或true for (IIS 7.5, 8)
  • Process Model Section-> Idle Timeout(minutes)设置为0(意思是:没有空闲超时)
  • Recycling部分 -> Regular time Interval设置为0(意思是:不回收)
    3. 将您的 Quartz 站点部署到该应用程序池中。并向池发送一个请求以“唤醒您的应用程序”,它会一直运行,直到您停止它为止。 在此处输入图片说明
    就是这样。
    更新:保持应用程序池始终处于活动状态的另一种解决方案是使用自动启动 ASP.NET 应用程序

另一种选择:使用一些第三方 ping 工具(如uptimerobotdiy one)每隔 x 秒(或分钟)不断刷新您的网站


Lef*_*tyX 4

我注意到的一件事是在您的 ASP.NET 应用程序中使用了Scheduler
您应该使用单例对象。

在你的process.aspx.cs这一行

IScheduler scheduler = new StdSchedulerFactory().GetScheduler();
Run Code Online (Sandbox Code Playgroud)

创建一个新的调度程序,但您应该使用您在Application_Start.

如果您想访问单例实例,请在您的Global.asax.cs

 public static ISchedulerFactory SchedulerFactory;
 public static IScheduler Scheduler;
Run Code Online (Sandbox Code Playgroud)

你可以在你的process.aspx.cs

MvcApplication.Scheduler.ScheduleJob(job, triggersSet, true);
Run Code Online (Sandbox Code Playgroud)

另一种解决方案是使用依赖注入。您可以在此处找到一些使用StructureMap 的信息,并在此处找到有关Unity的信息。

更新:

您可以在此处下载名为AspNet_Quartz 的示例应用程序 (asp.net 4.0) ,并在此处查看其工作原理。