在 Quartz 中,如何通知作业调度程序正在关闭?
In Quartz, how do I notify a job that the scheduler is shutting down?
我正在使用 Quartz.Net 在 Windows 服务中安排一些较长的 运行 作业。我正在尝试优雅地停止服务,例如准备重新启动时。我需要通知工作,以便他们可以根据离完成的程度来决定是完成还是中止。我已经查看了中断和侦听器,但我似乎无法弄清楚如何传达挂起的关闭。
我也曾尝试将长作业分解为更小的连续作业,但这样做会严重影响性能。
你提到了,但你测试过"void Interrupt()"是否在Scheduler关闭时被调用?
如果这不起作用,我认为没有内置方法。
但如果您想探索所有可能性,请获取 Quartz.Net 的源代码,而不是引用构建的库,将所有 (quartz.net).csproj 添加到您的 .sln 中,然后参考 "by project"...并以这种方式单步执行代码。或者至少查看源代码以收集线索。
那就追这个
namespace Quartz.Core
{
public class QuartzScheduler : MarshalByRefObject, IRemotableQuartzScheduler
{
{
public virtual void Shutdown(bool waitForJobsToComplete)
{
if (shuttingDown || closed)
{
return;
}
这是一个示例 IInterruptableJob。值得一试,看看该方法是否触发。您可能已经尝试过此操作,但由于您没有显示作业的代码,因此这是一个 "what did someone else" 示例。
public class AnInterruptableJob : IJob, IInterruptableJob
{
private bool _isInterrupted = false;
private int MAXIMUM_JOB_RUN_SECONDS = 10;
/// <summary>
/// Called by the <see cref="IScheduler" /> when a
/// <see cref="ITrigger" /> fires that is associated with
/// the <see cref="IJob" />.
/// </summary>
public virtual void Execute(IJobExecutionContext context)
{
/* See http://aziegler71.wordpress.com/2012/04/25/quartz-net-example/ */
JobKey key = context.JobDetail.Key;
JobDataMap dataMap = context.JobDetail.JobDataMap;
int timeOutSeconds = dataMap.GetInt("TimeOutSeconds");
if (timeOutSeconds <= 0)
{
timeOutSeconds = MAXIMUM_JOB_RUN_SECONDS;
}
Timer t = new Timer(TimerCallback, context, timeOutSeconds * 1000, 0);
Console.WriteLine(string.Format("AnInterruptableJob Start : JobKey='{0}', timeOutSeconds='{1}' at '{2}'", key, timeOutSeconds, DateTime.Now.ToLongTimeString()));
try
{
Thread.Sleep(TimeSpan.FromSeconds(7));
}
catch (ThreadInterruptedException)
{
}
if (_isInterrupted)
{
Console.WriteLine("Interrupted. Leaving Excecute Method.");
return;
}
Console.WriteLine(string.Format("End AnInterruptableJob (should not see this) : JobKey='{0}', timeOutSeconds='{1}' at '{2}'", key, timeOutSeconds, DateTime.Now.ToLongTimeString()));
}
// Timer t = new Timer(TimerCallback, context, 1000, 0);
private void TimerCallback(Object o)
{
IJobExecutionContext context = o as IJobExecutionContext;
if (null != context)
{
context.Scheduler.Interrupt(context.FireInstanceId);
}
}
public void Interrupt()
{
_isInterrupted = true;
Console.WriteLine(string.Format("AnInterruptableJob.Interrupt called at '{0}'", DateTime.Now.ToLongTimeString()));
}
}
}
我认为作业实例无法自行确定调度程序正在关闭。作业(工作者)线程和服务控制线程之间需要一些协调。
这是一个可中断作业示例 class。请注意使用 ManualResetEventSlim
表示已请求中断,并表示 Execute() 方法已相应退出。
注意:此代码基于源下载附带的 Quartz.Server.2010 示例项目代码构建。
/// <summary></summary>
[DisallowConcurrentExecution]
public class DumbInterruptableJob : IInterruptableJob, IDisposable
{
private static readonly ILog logger = LogManager.GetLogger(typeof(DumbInterruptableJob));
private ManualResetEventSlim _interruptRequestToken;
private ManualResetEventSlim _interruptCompleteToken;
/// <summary></summary>
public DumbInterruptableJob()
{
_interruptRequestToken = new ManualResetEventSlim(false);
_interruptCompleteToken = new ManualResetEventSlim(false);
}
/// <summary></summary>
/// <param name="context"></param>
public void Execute(IJobExecutionContext context)
{
try
{
JobKey key = context.JobDetail.Key;
logger.Info(m => m("Instance {0} of DumbInterruptableJob is working.", key));
// The work loop
for (int i = 0; i < 4; i++)
{
if (_interruptRequestToken.IsSet)
{
logger.Info(m => m("Work interrupt requested...Exiting.")); // Breakpoint #1
return;
}
logger.Info(m => m("Work..."));
Thread.Sleep(2000);
}
logger.Info(m => m("Work complete!"));
}
catch (Exception ex)
{
logger.Error(m => m(ex.Message));
}
finally
{
_interruptCompleteToken.Set();
}
}
/// <summary></summary>
public void Interrupt()
{
logger.Info(m => m("Setting interrupt flag..."));
_interruptRequestToken.Set();
logger.Info(m => m("Waiting for work thread to stop..."));
_interruptCompleteToken.Wait();
logger.Info(m => m("Work thread stopped.")); // Breakpoint #2
}
/// <summary></summary>
public void Dispose()
{
_interruptCompleteToken.Dispose();
_interruptRequestToken.Dispose();
}
}
下一步是在服务停止时中断(取消)所有 运行ning 个可中断作业。
public virtual void Stop()
{
try
{
// Calling Shutdown(false) stops the scheduler from starting new jobs,
// but the method doesn't block, allowing us to access any running jobs
// and attempt to cancel (interrupt) them.
logger.Info(m => m("Shutting down the scheduler..."));
scheduler.Shutdown(false);
var interruptableJobs = new List<Task>();
foreach (var ctx in scheduler.GetCurrentlyExecutingJobs())
{
if (ctx.JobInstance is IInterruptableJob)
{
interruptableJobs.Add(Task.Factory.StartNew(() =>
{
logger.Info(m => m("Waiting for interruptable job {0} to stop...", ctx.JobDetail.Key));
scheduler.Interrupt(ctx.JobDetail.Key);
logger.Info(m => m("Interruptable job {0} has stopped.", ctx.JobDetail.Key));
}));
}
}
if (interruptableJobs.Count > 0)
{
logger.Info(m => m("Waiting for all interruptable jobs to stop..."));
Task.WaitAll(interruptableJobs.ToArray());
logger.Info(m => m("All interruptable jobs have stopped."));
}
logger.Info(m => m("Waiting for all running jobs to complete..."));
scheduler.Shutdown(true);
logger.Info(m => m("All running jobs have completed. Scheduler shutdown complete.")); // Breakpoint #3
}
catch (Exception ex)
{
logger.Error(string.Format("Scheduler stop failed: {0}", ex.Message), ex);
throw;
}
logger.Info("Scheduler shutdown complete");
}
要查看同步操作,请在注释中指示的位置设置断点,运行它,然后关闭服务(如果 运行ning 从命令行按 Ctrl-C),并观察命令命中断点。
我正在使用 Quartz.Net 在 Windows 服务中安排一些较长的 运行 作业。我正在尝试优雅地停止服务,例如准备重新启动时。我需要通知工作,以便他们可以根据离完成的程度来决定是完成还是中止。我已经查看了中断和侦听器,但我似乎无法弄清楚如何传达挂起的关闭。
我也曾尝试将长作业分解为更小的连续作业,但这样做会严重影响性能。
你提到了,但你测试过"void Interrupt()"是否在Scheduler关闭时被调用?
如果这不起作用,我认为没有内置方法。
但如果您想探索所有可能性,请获取 Quartz.Net 的源代码,而不是引用构建的库,将所有 (quartz.net).csproj 添加到您的 .sln 中,然后参考 "by project"...并以这种方式单步执行代码。或者至少查看源代码以收集线索。
那就追这个
namespace Quartz.Core
{
public class QuartzScheduler : MarshalByRefObject, IRemotableQuartzScheduler
{
{
public virtual void Shutdown(bool waitForJobsToComplete)
{
if (shuttingDown || closed)
{
return;
}
这是一个示例 IInterruptableJob。值得一试,看看该方法是否触发。您可能已经尝试过此操作,但由于您没有显示作业的代码,因此这是一个 "what did someone else" 示例。
public class AnInterruptableJob : IJob, IInterruptableJob
{
private bool _isInterrupted = false;
private int MAXIMUM_JOB_RUN_SECONDS = 10;
/// <summary>
/// Called by the <see cref="IScheduler" /> when a
/// <see cref="ITrigger" /> fires that is associated with
/// the <see cref="IJob" />.
/// </summary>
public virtual void Execute(IJobExecutionContext context)
{
/* See http://aziegler71.wordpress.com/2012/04/25/quartz-net-example/ */
JobKey key = context.JobDetail.Key;
JobDataMap dataMap = context.JobDetail.JobDataMap;
int timeOutSeconds = dataMap.GetInt("TimeOutSeconds");
if (timeOutSeconds <= 0)
{
timeOutSeconds = MAXIMUM_JOB_RUN_SECONDS;
}
Timer t = new Timer(TimerCallback, context, timeOutSeconds * 1000, 0);
Console.WriteLine(string.Format("AnInterruptableJob Start : JobKey='{0}', timeOutSeconds='{1}' at '{2}'", key, timeOutSeconds, DateTime.Now.ToLongTimeString()));
try
{
Thread.Sleep(TimeSpan.FromSeconds(7));
}
catch (ThreadInterruptedException)
{
}
if (_isInterrupted)
{
Console.WriteLine("Interrupted. Leaving Excecute Method.");
return;
}
Console.WriteLine(string.Format("End AnInterruptableJob (should not see this) : JobKey='{0}', timeOutSeconds='{1}' at '{2}'", key, timeOutSeconds, DateTime.Now.ToLongTimeString()));
}
// Timer t = new Timer(TimerCallback, context, 1000, 0);
private void TimerCallback(Object o)
{
IJobExecutionContext context = o as IJobExecutionContext;
if (null != context)
{
context.Scheduler.Interrupt(context.FireInstanceId);
}
}
public void Interrupt()
{
_isInterrupted = true;
Console.WriteLine(string.Format("AnInterruptableJob.Interrupt called at '{0}'", DateTime.Now.ToLongTimeString()));
}
}
}
我认为作业实例无法自行确定调度程序正在关闭。作业(工作者)线程和服务控制线程之间需要一些协调。
这是一个可中断作业示例 class。请注意使用 ManualResetEventSlim
表示已请求中断,并表示 Execute() 方法已相应退出。
注意:此代码基于源下载附带的 Quartz.Server.2010 示例项目代码构建。
/// <summary></summary>
[DisallowConcurrentExecution]
public class DumbInterruptableJob : IInterruptableJob, IDisposable
{
private static readonly ILog logger = LogManager.GetLogger(typeof(DumbInterruptableJob));
private ManualResetEventSlim _interruptRequestToken;
private ManualResetEventSlim _interruptCompleteToken;
/// <summary></summary>
public DumbInterruptableJob()
{
_interruptRequestToken = new ManualResetEventSlim(false);
_interruptCompleteToken = new ManualResetEventSlim(false);
}
/// <summary></summary>
/// <param name="context"></param>
public void Execute(IJobExecutionContext context)
{
try
{
JobKey key = context.JobDetail.Key;
logger.Info(m => m("Instance {0} of DumbInterruptableJob is working.", key));
// The work loop
for (int i = 0; i < 4; i++)
{
if (_interruptRequestToken.IsSet)
{
logger.Info(m => m("Work interrupt requested...Exiting.")); // Breakpoint #1
return;
}
logger.Info(m => m("Work..."));
Thread.Sleep(2000);
}
logger.Info(m => m("Work complete!"));
}
catch (Exception ex)
{
logger.Error(m => m(ex.Message));
}
finally
{
_interruptCompleteToken.Set();
}
}
/// <summary></summary>
public void Interrupt()
{
logger.Info(m => m("Setting interrupt flag..."));
_interruptRequestToken.Set();
logger.Info(m => m("Waiting for work thread to stop..."));
_interruptCompleteToken.Wait();
logger.Info(m => m("Work thread stopped.")); // Breakpoint #2
}
/// <summary></summary>
public void Dispose()
{
_interruptCompleteToken.Dispose();
_interruptRequestToken.Dispose();
}
}
下一步是在服务停止时中断(取消)所有 运行ning 个可中断作业。
public virtual void Stop()
{
try
{
// Calling Shutdown(false) stops the scheduler from starting new jobs,
// but the method doesn't block, allowing us to access any running jobs
// and attempt to cancel (interrupt) them.
logger.Info(m => m("Shutting down the scheduler..."));
scheduler.Shutdown(false);
var interruptableJobs = new List<Task>();
foreach (var ctx in scheduler.GetCurrentlyExecutingJobs())
{
if (ctx.JobInstance is IInterruptableJob)
{
interruptableJobs.Add(Task.Factory.StartNew(() =>
{
logger.Info(m => m("Waiting for interruptable job {0} to stop...", ctx.JobDetail.Key));
scheduler.Interrupt(ctx.JobDetail.Key);
logger.Info(m => m("Interruptable job {0} has stopped.", ctx.JobDetail.Key));
}));
}
}
if (interruptableJobs.Count > 0)
{
logger.Info(m => m("Waiting for all interruptable jobs to stop..."));
Task.WaitAll(interruptableJobs.ToArray());
logger.Info(m => m("All interruptable jobs have stopped."));
}
logger.Info(m => m("Waiting for all running jobs to complete..."));
scheduler.Shutdown(true);
logger.Info(m => m("All running jobs have completed. Scheduler shutdown complete.")); // Breakpoint #3
}
catch (Exception ex)
{
logger.Error(string.Format("Scheduler stop failed: {0}", ex.Message), ex);
throw;
}
logger.Info("Scheduler shutdown complete");
}
要查看同步操作,请在注释中指示的位置设置断点,运行它,然后关闭服务(如果 运行ning 从命令行按 Ctrl-C),并观察命令命中断点。