【问题标题】:Quartz.NET scheduler doesn't fire jobs/triggers once deployedQuartz.NET 调度程序一旦部署就不会触发作业/触发器
【发布时间】:2013-08-12 21:07:44
【问题描述】:

简介

我在 ASP.Net 框架 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

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);
}

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 的建议,更改了 singleton 的调度程序使用,但在远程服务器上仍然获得相同的行为。

更新 2

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

【问题讨论】:

  • 如果您认为我的回答对您来说足够好,请不要忘记接受它。谢谢。
  • LeftyX,您对您的帮助非常友好,我会看看是否没有其他建议,如果没有,我会接受您的回答。另外,我试图支持你,但我没有足够的声誉。请在下面查看我的问题并进行更新。
  • Zed,你为什么不尝试发布我的示例代码(你可以从我的 skydrive 存储库下载它)并看看它是如何工作的?这可能是托管服务提供商的问题...
  • quartz.threadPool.threadCount 有什么价值? (不知道是否可以将其设置为零,但如果可以,可能系统认为它没有空闲线程来执行作业)。
  • @sgmoore,quartz.threadPool.threadCount 默认为 10,我还测试了将其更改为 5。我可以在日志中看到它已成功更改,其中显示 Using thread pool ' Quartz.Simpl.SimpleThreadPool' - 有 5 个线程。

标签: asp.net quartz.net


【解决方案1】:

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

  1. 转到 IIS 管理器 -> 应用程序池 -> 创建一个新池,我将其命名为调度程序(任何名称都可以)
  2. 选择调度程序池 -> 高级设置
  • General 部分的 Start Mode 中,选择 AlwaysRunning (IIS 8.5) 或 true for (IIS 7.5, 8)
  • 流程模型部分-> 空闲超时(分钟)设置为0(意思是:无空闲超时)
  • 回收部分-> 定期间隔设置为0(意思是:不回收)
    3。将您的 Quartz 站点部署到该应用程序池中。并向池发送一个请求以“唤醒您的应用程序”,它将一直运行直到您停止它。
    就是这样。
    更新:让您的应用程序池始终处于活动状态的另一个解决方案是使用 Auto-Start ASP.NET Applications

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

【讨论】:

  • 那个配置是​​唯一有效的:)
【解决方案2】:

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

在你的process.aspx.cs这一行

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

创建一个新的调度器,但你应该使用你在Application_Start中创建的静态调度器。

如果您想访问单例实例,请在 Global.asax.cs 中使用公共成员:

 public static ISchedulerFactory SchedulerFactory;
 public static IScheduler Scheduler;

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

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

另一种解决方案是使用依赖注入。您可以使用 StructureMaphere for Unity 找到一些信息 here

更新:

您可以下载一个名为 AspNet_Quartz here 的示例应用程序 (asp.net 4.0),并查看它是如何工作的 here

【讨论】:

  • 感谢您的帮助。通过阅读有关石英调度程序的信息,我认为它自己管理单例,并且每次调用 StdSchedulerFactory().GetScheduler(); 时都会检索它。您能否指出我将调度程序用作单例的正确方向?谢谢!
  • 谢谢。您的更新是指 MVC,这是网络表单。无论如何,我发现,就像你说的那样,将“public”添加到 global.asax 中声明的变量中,然后我可以像这样从 aspx.cs 访问它(单例): ASP.global_asax.Scheduler 尽管如此,全局行为是一样的:本地工作就像一个魅力,一旦上传到远程服务器,调度程序似乎没有像描述的那样监听触发器,并且 CPU 开始疯狂地工作,达到 50%。我是否可能需要在托管环境上设置特殊权限或类似权限?
  • @zed:Quartz.net 必须在中等信任的环境中工作。您使用的是最新版本之一吗?
  • 如果我进入 IIS(托管环境)并在网站 .Net Trust Levels 上查看,它会显示“完整(内部)”。与本地服务器相同。 Quartz 版本是 2.1.2,我相信这是 .net 的最新版本
  • @zed:我不知道你的问题是什么。我创建了一个测试项目。检查我更新的答案。
【解决方案3】:

问题与IIS有关,而不是与调度程序Quartz.NETHangfire等有关。另一方面,网上发布了很多解决方法,但只有一部分有效。在我看来,没有必要应用大量的配置设置。只需在您发布应用程序的服务器上安装Keep Alive Service For IIS 6.0/7.5 即可享受。之后,您发布的应用程序将在应用程序池回收、IIS/应用程序重启等之后仍然存在。希望这会有所帮助...

【讨论】:

  • 很好的答案,就是这样。 IIS 回收应用程序池,并在不活动时关闭。这就是为什么石英重复时间表不会触发的原因。检查您的 IIS 应用程序池设置!!!
【解决方案4】:

我刚刚遇到了一个可能会咬别人的类似问题 - 在打开调试并收到“批量获取 0 个触发器”消息后,它让我想到了这个 SO 问题。
我有一份工作,每 2 小时一次,如下所示:

"0 0 0/2 * * ?"

我想让它更频繁,所以每 2 分钟这样一次:

"0 0/2 0 * ?"

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

"0 0/2 * * ?"

所以教训是,当“左移”你的 cron 时,用 * 回填,而不是 0。

希望对其他人有所帮助。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-08-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多