2014-02-25 95 views
-1

主題:預定作業任務

我想實現一個基本的作業調度在Java中處理經常堅持計劃任務(用於個人學習計劃)。我不想使用Quartz/Obsidian/Cron4J /等任何(隨時可用)庫。

目的:

  • 工作必須是持久的(處理服務器關閉)
  • 作業執行時間可能需要長達2-5〜MN。
  • 管理大量工作
  • 多線程
  • 輕快的;)

所有我的工作是在一個MySQL數據庫。

JOB_TABLE (id, name, nextExecution,lastExecution, status(IDLE,PENDING,RUNNING)) 

一步一步:

  1. 從「JOB_TABLE」,其中「nextExecution > now」 AND 「status = IDLE「檢索每個作業。該步驟每10mn由一個線程執行。

  2. 對於檢索的每個作業,我在ThreadPoolExecutor中放入一個新線程,然後在我的「JOB_TABLE」中將作業狀態更新爲「PENDING」。

  3. 作業線程正在運行時,我將作業狀態更新爲「RUNNING」。

  4. 當工作結束時,我更新lastExecution與當前的時間,我設定一個新的nextExecution時間,我換了工作狀態爲「IDLE」。

當服務器啓動時,我將每個PENDING/RUNNING作業放在ThreadPoolExecutor中。

問題/觀察:

  • 第2步:請問ThreadPoolExecutor的處理大量的線程(約20000)?
  • 我應該使用NoSQL解決方案而不是MySQL嗎?
  • 這是處理這種用例的最佳解決方案嗎?

這是一個草稿,沒有代碼背後。我願意提出建議,評論和批評!

+2

「我不希望使用任何(即用型)庫的石英/黑曜石/ Cron4J的/ etc」 - 爲什麼不呢? –

+0

因爲這是一個研究項目,以提高我的Java知名度! ;) – user3350705Ol

+0

它然後分析這些庫的代碼。那麼你可以學到的不僅僅是重新發明輪子。 –

回答

2

我在一個真實的項目上做了類似於你的任務,但在.NET中。以下是我可以回憶的關於您的問題:

步驟2:請問ThreadPoolExecutor會處理大量的線程(〜20000)嗎?

我們發現.NET的內置線程池是最糟糕的方法,因爲該項目是一個Web應用程序。原因:Web應用程序依賴於內置的線程池(這是靜態的,因此在運行進程中爲所有用途共享)在單獨的線程中運行每個請求,同時保持線程的有效回收。爲我們的內部處理使用相同的線程池將耗盡它,並且不會爲用戶請求留下任何空閒線程,或損害它們的性能,這是不可接受的。

由於您似乎在運行相當多的工作(20k對於單臺機器來說很多),您絕對應該尋找一個自定義線程池。沒有必要寫你自己的,我打賭有現成的解決方案,寫一個遠遠超出你的研究項目將要求* 看到評論(如果我理解你正在做一所學校或大學項目)。

我應該使用NoSQL解決方案而不是MySQL嗎?

取決於。您顯然需要同時更新作業狀態,因此,您將可以同時訪問多個線程中的單個表。數據庫可以很好地擴展,假設你做對了。以下是我指的是這樣做的權利:

  • 設計你的代碼的方式,每個職位將隻影響其在數據庫中的行自己的子集(包括其它表)。如果您能夠這樣做,則不需要對數據庫級別進行任何明確的鎖定(以事務序列化級別的形式)。你甚至可以執行一個自由的序列化級別,可以允許髒或幻讀 - 這將更快。但是要小心,您必須小心確保沒有作業將會出現同一行。這在現實生活中很難實現,因此您應該在數據庫鎖定中尋找其他方法。

  • 使用適當的事務序列化模式。事務序列化模式定義了數據庫級別的鎖定行爲。您可以將其設置爲鎖定整個表格,只鎖定您影響的行,或者根本不鎖定。明智地使用它,因爲任何濫用都可能影響整個應用程序或數據庫服務器的數據一致性,完整性和穩定性。

  • 我對NoSQL數據庫並不熟悉,因此我只能建議您研究併發功能並將它們映射到您的方案。你可能會得到一個非常合適的解決方案,但你必須根據你的需求來檢查。從您的描述中,您將不得不支持通過同一類型的對象同時進行數據操作(表格的模擬是什麼)。

它是處理此類使用案例的最佳解決方案?

是和

  • 號,因爲你會遇到的開發商都面臨着現實世界的艱鉅任務之一。我曾與同事有超過3倍的自己的經驗,他們比我更不願意做多線程任務,他們真的很討厭這一點。如果你覺得這個區域對你來說很有意思,那就隨它玩吧,儘可能多地學習和改進。

  • ,因爲如果你正在從事一個真實的項目,你需要一些可靠的項目。如果你有這麼多的問題,你顯然需要時間來成熟,並能夠爲這樣的任務提供一個穩定的解決方案。多線程是有很多原因一個棘手的問題:

    • 這是很難調試
    • 它引入了很多失敗的點,你需要知道它們的所有
    • 這可能是一個痛苦其他開發人員協助或使用您的代碼,除非您遵守普遍接受的規則。
    • 錯誤處理可能會很棘手
    • 行爲是不可預知/不確定的。

    現有的解決方案具有高水平的成熟度和可靠性,是實際項目的首選方法。缺點是你必須學習它們,並檢查它們是如何定製的,以滿足你的需求。

無論如何,如果你需要做你的方式,然後你的端口,以實現一個真實的項目,或您自己的項目,我可以建議你這樣做可插拔的方式。使用抽象,編程到接口和其他實踐將您自己的特定實現與將設置預定作業的邏輯分離。這樣,如果這成爲一個問題,您可以調整您的API到現有的解決方案。


而在去年,但並非最不重要,我沒有看到你身邊的任何錯誤處理的預測。思考和研究如果工作失敗應該怎麼做。在這種情況下,至少應添加「失敗」狀態或持續存在。涉及到線程時,錯誤處理是棘手的,所以要徹底研究您的研究和實踐。

好運

+1

謝謝它幫助我! – user3350705Ol

+0

很高興知道:) –

1

您可以使用ThreadPoolExecutor#setMaximumPoolSize(int)聲明最大池大小。由於Integer.MAX大於20000,因此技術上可以。

另一個問題是,你的機器會支持這麼多的線程運行。您將提供足夠的RAM,以便每個階段都將分配到堆棧上。

在現代臺式機或筆記本電腦上,您不應該對address ~20,000 threads問題,但在移動設備上,這可能是個問題。

從DOC:

核心和最大池尺寸

甲的ThreadPoolExecutor將自動 調整池大小(參見getPoolSize())根據邊界由corePoolSize設置 (見getCorePoolSize ())和maximumPoolSize(請參閱 getMaximumPoolSize())。當在方法 execute(java.lang.Runnable)中提交新任務並且少於corePoolSize線程 正在運行時,即使其他 工作線程處於空閒狀態,也會創建一個新線程來處理請求。如果存在多於corePoolSize,但比最大池大小線程少的 ,則只有在隊列滿時,纔會創建一個新線程 。通過設置corePoolSize和maximumPoolSize 相同,您可以創建一個固定大小的線程池。通過將 maximumPoolSize設置爲一個基本上無界的值,例如 Integer.MAX_VALUE,您允許該池容納任意併發任務數 。最典型的是,核心和最大池大小隻在構建時設置,但也可以使用setCorePoolSize(int)和setMaximumPoolSize(int)動態更改 。

More

關於DB。創建一個不依賴於數據庫結構的解決方案。然後你可以設置兩個環境並測量它。從你知道的技術開始。但請保持開放的其他解決方案。在開始時,關係數據庫應該跟上表現。如果你正確地管理它,它不應該成爲一個問題。 NoSQL用於處理真正的大數據。但最適合你的是創建並運行一些性能測試。

+0

在內存方面,我會在ThreadPool隊列中遇到1M作業問題嗎? - 你對數據庫抽象是正確的,但根據你的經驗,我會處理這種架構的一些問題嗎? (在小型虛擬機上) – user3350705Ol

+0

請參閱編輯。但是,它的基礎是它取決於。關於設計我會說同樣的。它取決於你如何調整數據庫。在這一點上,你應該從你的想法開始。然後衡量它並嘗試以百分比的方式提高。你項目的主要目標是在有效的時刻執行任務,它將如何完成只是技術細節。拉一些關於線程管理的講座,然後開始創建一個原型。 –

+0

感謝您的幫助! – user3350705Ol