簡體   English   中英

預定的工作任務

[英]Scheduled Job Task

學科:

我正在嘗試在Java中實現基本的作業計划,以處理重復的持久性計划任務(針對個人學習項目)。 我不想使用任何(即用型)庫,例如Quartz / Obsidian / Cron4J / etc。

目的:

  • 作業必須是持久的(以處理服務器關閉)
  • 作業執行時間最多可能需要2到5百萬。
  • 處理大量工作
  • 多線程
  • 輕便快捷;)

我所有的工作都在MySQL數據庫中。

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

一步步:

  1. 從“ JOB_TABLE “nextExecution > now” AND “status = IDLE“JOB_TABLE ”中檢索每個作業。 每10秒鍾由一個線程執行此步驟。

  2. 對於每個檢索到的作業,我在ThreadPoolExecutor放置了一個新線程,然后在“ JOB_TABLE ”中將作業狀態更新為“ PENDING ”。

  3. 當作業線程正在運行時,我將作業狀態更新為“ RUNNING ”。

  4. 作業完成后,我將使用當前時間更新lastExecution ,設置新的nextExecution時間,並將作業狀態更改為“ IDLE ”。

服務器啟動時,我將每個PENDING / RUNNING作業放入ThreadPoolExecutor

問題/觀察:

  • 步驟2:ThreadPoolExecutor是否可以處理大量線程(〜20000)?
  • 我應該使用NoSQL解決方案代替MySQL嗎?
  • 這是處理此類用例的最佳解決方案嗎?

這是草稿,沒有任何代碼。 我願意提出建議,評論和批評!

在.NET中,我已經完成了與您在實際項目中相似的任務。 關於您的問題,這是我能想到的:

步驟2:ThreadPoolExecutor是否可以處理大量線程(〜20000)?

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

由於您似乎正在運行大量作業(一台機器需要20k,所以很多工作),所以您絕對應該尋找自定義線程池。 不過,您無需自己編寫,我敢打賭,這里有現成的解決方案,並且編寫的解決方案遠遠超出您的研究項目的要求*。 請參閱評論 (如果我正確理解您正在做的是學校或大學的項目)。

我應該使用NoSQL解決方案代替MySQL嗎?

要看。 顯然,您需要同時更新作業狀態,因此,您將可以同時從多個線程訪問一個表。 假設您做對了,數據庫可以很好地擴展。 這就是我所說的正確執行的操作:

  • 設計代碼的方式應使每個作業僅影響數據庫中它自己的行子集(包括其他表)。 如果可以,則不需要在數據庫級別上任何顯式的鎖定(以事務序列化級別的形式)。 您甚至可以強制執行自由序列化級別,該級別可能允許進行臟讀或幻像讀取-執行速度更快。 但是要當心 ,您必須仔細確保在同一行上沒有作業並發。 在現實項目中很難做到這一點,因此您可能應該在數據庫鎖定中尋找替代方法。

  • 使用適當的事務序列化模式。 事務序列化模式在數據庫級別定義鎖定行為。 您可以將其設置為鎖定整個表,僅鎖定受影響的行或什么都不鎖定。 明智地使用它,因為任何濫用都會影響整個應用程序或數據庫服務器的數據一致性,完整性和穩定性。

  • 我對NoSQL數據庫不熟悉,因此我只能建議您研究並發功能並將其映射到您的方案。 您最終可能會找到一個非常合適的解決方案,但必須根據需要進行檢查。 根據您的描述,您將必須支持在相同類型的對象(表的模擬物)上同時進行數據操作。

這是處理此類用例的最佳解決方案嗎?

是和否

  • 是的 ,因為您將遇到開發人員在現實世界中面臨的困難任務之一。 我與同事的合作經驗是我的3倍以上,他們比我更不願意執行多線程任務,他們真的很討厭。 如果您覺得這個領域很有趣,請嘗試並學習,並盡可能多地提高自己。

  • ,因為如果您正在做一個真實的項目,則需要可靠的東西。 如果您有很多問題,顯然您將需要時間來成熟,並且能夠為該任務提供穩定的解決方案。 多線程是一個困難的話題,原因有很多:

    • 很難調試
    • 它引入了許多故障點,您需要了解所有這些點
    • 除非您遵守公認的規則,否則其他開發人員可能難以協助或使用您的代碼。
    • 錯誤處理可能很棘手
    • 行為是不可預測的/不確定的。

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

無論如何,如果您需要按自己的方式做,然后將成就移植到一個真實的項目或您自己的項目中,我可以建議您以可插拔的方式進行。 使用抽象, 接口編程和其他實踐將您自己的特定實現與設置計划的作業的邏輯脫鈎。 這樣,如果這成為問題,則可以使您的api適應現有解決方案。


最后但並非最不重要的一點是 ,我沒有看到任何錯誤處理方面的預測。 思考並研究如果工作失敗了該怎么辦。 至少添加“失敗”狀態或在這種情況下可以保留的狀態。 當涉及到線程時,錯誤處理非常棘手,因此請仔細研究和實踐。

祝好運

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

另一個問題是您的計算機是否支持這么多線程來運行。 您將提供足夠的RAM,以便每個踏步都將在堆棧上分配。

在現代台式機或筆記本電腦上處理約20,000個線程應該不成問題,但在移動設備上可能是個問題。

從文檔:

核心和最大池大小

ThreadPoolExecutor將根據corePoolSize(請參見getCorePoolSize())和maximumPoolSize(請參見getMaximumPoolSize())設置的邊界自動調整池大小(請參見getPoolSize())。 當在方法execute(java.lang.Runnable)中提交新任務,並且正在運行的線程少於corePoolSize線程時,即使其他工作線程處於空閑狀態,也會創建一個新線程來處理請求。 如果運行的線程數大於corePoolSize但小於maximumPoolSize,則僅在隊列已滿時才創建新線程。 通過將corePoolSize和maximumPoolSize設置為相同,可以創建固定大小的線程池。 通過將maximumPoolSize設置為一個本質上不受限制的值(例如Integer.MAX_VALUE),可以允許池容納任意數量的並發任務。 通常,核心和最大池大小僅在構造時設置,但也可以使用setCorePoolSize(int)和setMaximumPoolSize(int)動態更改。

更多

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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM