出于测试原因,我希望能够调整Quartz.Net目前认为的时间,因此我不必等待数小时,数天或数周才能检查我的代码是否正常工作。
为此我创建了以下简单函数(它在F#中,但可以用C#或其他语言轻松完成):
let SimulateTime = fun () ->
currentTime <- DateTimeOffset.UtcNow
timeDifferenceInSeconds <- (currentTime - lastCheckedTime).TotalSeconds
simulatedTime <- simulatedTime.AddSeconds((timeDifferenceInSeconds *scaleTimeBy))
lastCheckedTime <- currentTime
simulatedTime
其中currentTime,lastCheckedTime和simulatedTime都是DateTimeOffset类型,timeDifferenceInSeconds和scaleTimeBy都是float类型。
然后我更改SystemTime.Now和SystemTime.UtcNow以使用上述函数,如下所示:
SystemTime.Now <-
Func<DateTimeOffset>(
fun () -> SimulateTime())
SystemTime.UtcNow <-
Func<DateTimeOffset>(
fun () -> SimulateTime())
Mark Seemann在我之前的一个问题中可以找到here。
现在这种情况大部分都有效,但似乎较长的功能会导致它以相当宽的幅度关闭。我的意思是我的所有触发器都会失火。例如,如果我将触发器设置为每小时发生一次并将scaleTimeBy设置为60.0,以便每秒传递计数为一分钟,它将永远不会实际触发。如果我有一个失火策略,那么触发器可以关闭,但它激活时所列出的时间将延迟到半小时标记(因此比本例中的应用速度慢了整整30秒)。
但我可以这样做:
Console.WriteLine(SimulateTime())
Thread.Sleep(TimeSpan.FromSeconds(60.0))
Console.WriteLine(SimulateTime())
在这个例子中输出到屏幕的两次之间的差异恰好是一个小时,因此呼叫似乎不应该添加比它更多的时间差。
任何人对如何解决此问题或处理此问题的更好方法有任何建议?
编辑: 因此,SimulateTime函数的C#版本将是这样的:
public DateTimeOffset SimulateTime() {
currentTime = DateTimeOffset.UtcNow;
double timeDifference = (currentTime - lastCheckedTime).TotalSeconds;
simulatedTime = simulatedTime.AddSeconds(timeDifference * scaleTimeBy);
lastCheckedTime = currentTime
return simulatedTime;}
如果这有助于任何人解决此问题。
答案 0 :(得分:1)
所以这个问题是由于Quartz.net会在任何时候都没有发生任何触发而无法拨打太多电话的情况下闲置等待而导致的失误。默认情况下,如果在时间跨度内没有发生任何触发,则等待约30秒。 idleWaitTime变量是QuartzSchedulerThread中设置的Timespan。现在,当检查可能很快发生的触发器时,它还使用QuartzSchedulerResources中的BatchTimeWIndow。
idleWaitTime和BatchTimeWindow都可以在配置/属性文件中设置,在这些文件中,可以调用它们,然后调用#34; org.quartz.scheduler.idleWaitTime&#34;和&#34; org.quartz.scheduler.batchTriggerAcquisitionFireAheadTimeWindow。&#34;
基于在BatchTimeWindow中调用的内容,我认为抓住变量只是一点前瞻(这是因为如果我加快速度,我想要一个小的idleWaitTime但是我希望它能够进一步查看触发因素,因为您等待的几秒钟实际上是几分钟,因此会比它想象的更早触发),但是&#34; org.quartz.scheduler.batchTriggerAcquisitionFireAheadTimeWindow&#34;在翻过配置属性的页面上意味着它可能导致事情早期触发并且不太准确。所以从这里开始是仅修改idleWaitTime的代码
let threadpool = Quartz.Simpl.SimpleThreadPool()
let jobstore = Quartz.Simpl.RAMJobStore()
let idleWaitTime = TimeSpan.FromSeconds(30.0/scaleTimeBy)
let dbfailureretryinverval = TimeSpan(int64 15000)
Quartz.Impl.DirectSchedulerFactory.Instance.CreateScheduler("TestScheduler","TestInstance",threadpool,jobstore,idleWaitTime,dbfailureretryinverval)
let scheduler = Quartz.Impl.DirectSchedulerFactory.Instance.GetScheduler("TestScheduler")
您可以使用DirectSchedulerFactory创建一个具有所需idleWaitTime的Scheduler,它可能会使用更好的文档。根据您正在处理的内容,您可能会或可能不想修改一些内容。对于线程池我只使用Quartz.net的默认SimpleThreadPool,因为我不关心此时搞乱线程,并且不想解释你如何这样做,除非这是问题的全部要点。有关工作场所的信息可用here。我在这里使用RAMJobStore因为它比AdoJobStore简单,但它对于这个例子并不重要。 dbfailureretryinterval是另一个不关心此示例的值,因此我只是查看默认情况下设置的内容。它的值对于此示例而言至关重要,因为它不连接到数据库。对于idleWaitTime可能想要做更多的测试来找出它的好价值,但我选择只使用scaleTimeBy缩放其默认值30秒,因为这是我用来扩展速度的东西正在经历。所以这应该是这样的,如果我让程序以更快的速度模拟时间,那么它应该只在较短的时间内保持空闲状态。需要注意的一件重要事情是,当以这种方式创建调度程序时,它也不会被返回,因此需要单独调用以获取我刚刚创建的调度程序。我不知道为什么会这样,我猜测如果你创建了几个调度程序而不一定使用所有调度程序,那就更好了。
现在毕竟你可能仍然会有一点失火率。虽然现在它的空闲时间要小得多(只有几秒钟,所以可能是一个可接受的余量,具体取决于你的用例),但它仍然存在问题,那就是它只是检查它是否有一个即将到来的触发器接下来的几分之一。
那么让我们看看为BatchTimeWindow添加时间是否有帮助?
let threadpool = Quartz.Simpl.SimpleThreadPool()
let threadexecutor = Quartz.Impl.DefaultThreadExecutor()
let jobstore = Quartz.Simpl.RAMJobStore()
let schedulepluginmap = System.Collections.Generic.Dictionary<String,Quartz.Spi.ISchedulerPlugin>()
let idleWaitTime = TimeSpan.FromSeconds(30.0/timeScale)
let maxBatchSize = 1
let batchTimeWindow = TimeSpan.FromSeconds(timeScale)
let scheduleexporter = Quartz.Simpl.RemotingSchedulerExporter()
Quartz.Impl.DirectSchedulerFactory.Instance.CreateScheduler("TestScheduler","TestInstance",threadpool,threadexecutor,jobstore,schedulepluginmap,idleWaitTime,maxBatchSize,batchTimeWindow,scheduleexporter)
let scheduler = Quartz.Impl.DirectSchedulerFactory.Instance.GetScheduler("TestScheduler")
现在这里有更多的变量,对于这个例子来说并不是真正关心的,并且甚至不用担心因为调整batchTimeWindow会让事情变得更糟。就像让你回到30分钟失误一样。所以不,batchTimeWindow虽然看起来可能有用但不是。只修改idleWaitTime。
理想情况下,对于这种使用,需要较短的等待时间和较大的预测时间,但是它的选项似乎不可用。