2015-04-21 33 views
0

由於測試的原因,我希望能夠調整Quartz.Net目前認爲它是什麼時間,所以我不一定要等待幾小時,幾天或幾周爲了檢查我的代碼是否正常工作。Quartz.net - 調整和加速SystemTime導致失火的問題

爲此我創建了這個簡單的函數(它是在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使用上述功能如下:這是在我的前面的問題,可以找到here所示馬克塞曼

SystemTime.Now <- 
    Func<DateTimeOffset>(
     fun() -> SimulateTime()) 
SystemTime.UtcNow <- 
    Func<DateTimeOffset>(
     fun() -> SimulateTime()) 

現在,這主要是工作,除了它看起來像更長的功能導致它由一個相當寬的邊緣關閉。我的意思是,我的所有觸發器都會失火。例如,如果我有一個觸發器設置爲每小時發生一次,並將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

雖然我現在沒有任何關於Quartz.Net,這聽起來像Quartz.Net,或您使用它的方式問題。我猜想有更多的C#開發人員比F#開發人員更熟悉這個庫,所以如果你用C#重現問題,可能有更好的機會得到答案,並且在這裏發佈C#代碼而不是F#... –

+0

以上幫助更多? – Sean

+0

還決定了是否可以通過在調用模擬時間函數時將timeDifference與當前時間一起打印出來來弄清楚事情。看起來Quartz.net通常會在幾乎同一時間進行5次調用,然後等待另一組調用。當測試這個等待時間似乎在大約20秒和大約0.001秒之間交替(主要問題中提到的30秒看起來更像是一個異常異常值,儘管20秒仍然很糟糕),任何人都知道這是爲什麼? – Sean

回答

0

所以這個問題是由於Quartz.net會在它認爲它沒有任何時間發生任何觸發器時會閒置並等待以避免調用過多而引起的。默認情況下,如果在時間範圍內沒有任何觸發器,則等待約30秒。 idleWaitTime變量是QuartzSchedulerThread中設置的Timespan。現在,當檢查可能很快發生的觸發器時,它還使用QuartzSchedulerResources中的BatchTimeWIndow。

idleWaitTime和BatchTimeWindow都可以在配置/屬性文件中被設置爲「org.quartz.scheduler.idleWaitTime」和「org.quartz.scheduler.batchTriggerAcquisitionFireAheadTimeWindow」。

根據它在BatchTimeWindow中調用的內容,我認爲這僅僅是抓取一個變量的前瞻(如果我加快速度,我想要一個小的idleWaitTime,但我想它看起來更接近觸發器,因爲幾秒鐘你的等待實際上是幾分鐘,所以會比它想象的更早觸發),但是描述了「org.quartz.scheduler。batchTriggerAcquisitionFireAheadTimeWindow」網頁上的去在配置屬性意味着,它可能會導致事情早期火災並不太準確,所以從這裏開始是剛剛修改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") 

您可以創建具有idleWaitTime你一個調度程序代碼想要通過使用DirectSchedulerFactory,這可能會使用一些更好的文檔。它也需要一些你可能或不想修改的東西,這取決於你在做什麼。對於線程池,我只是使用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。

理想情況下,這種使用會需要一個小的等待時間和更大的展望時間,但選項似乎並不像它的可用。

+0

所以這就是我所能弄明白的,我不會將它標記爲我接受的答案,以防萬一有更好的東西。 – Sean