[英]Dynamic Scheduled Concurrent Task Execution in Java
我正在嘗試實現一個基於某些用戶輸入對任務進行編程的應用程序。 用戶可以通過與IP地址關聯的telnet命令(一對一關系),執行頻率和2個組(集群,objectClass)來放置多個IP。
用戶應該能夠在運行時添加/刪除IP,群集,命令等。 他們還應該能夠中斷執行。
該應用程序應該能夠將telnet命令發送到IP,等待響應並將響應基於頻率保存在數據庫中。 我遇到的問題是嘗試使所有這些都成為多線程,因為telnet至少有60,000個IP,而在單個線程中完成將花費太多時間。 一個線程應在同一集群中使用相同的objectClass處理一組IP。
我看過Quartz來安排工作。 使用Quartz,我嘗試進行一個動態工作,該工作需要一個IP列表(帶有命令),對其進行處理並將結果保存到數據庫中。 但是后來我遇到了用戶給定的不同計時器的問題。 Quartz網頁上的示例是不完整的,並且不會太詳細。
然后,我嘗試使用Java Threads以老式的方式進行操作,但是我需要進行異常處理和參數傳遞,而Threads不這樣做。 然后我發現了Callables和Executors,但是我無法安排Callables的任務。
所以現在我很困惑,我該怎么辦?
好,這里有一些想法。 服用必要的鹽粒。
首先,創建所需要做的所有工作的清單。 我假設您在某處的表中有此連接,並且可以進行如下所示的連接:
cluster | objectClass | ip-address | command | frequency | last-run-time
這代表了系統需要完成的所有工作。 為了說明起見,我將說頻率可以采取“每天1次”,“每小時1次”,“每小時4次”,“每分鍾”的形式。 該表每行有一行(集群,objectClass,ip地址,命令)。 假設另一個表具有運行歷史,並帶有錯誤消息和其他內容。
現在您需要做的就是閱讀該表,並安排工作。 要進行調度,請使用以下方法之一:
ScheduledExecutorService exec = Executors...
當您安排某事時,您需要告訴它多久運行一次(使用我們給定的頻率就足夠容易了)和一個延遲。 如果某件事每分鍾運行一次,並且最后一次運行是在30分鍾前4分鍾,那么初始延遲為零。 如果每小時要運行一些東西,則初始延遲為(60分鍾-4.5分鍾= 55.5分鍾)。
ScheduledFuture<?> handle = exec.scheduleAtFixedRate(...);
更復雜的調度類型是為什么存在Quartz之類的原因,但是基本上,您只需要一種方法即可解決(調度,上次運行)下一次執行所花費的時間。 如果可以這樣做,則可以使用schedule(...)而不是scheduleAtFixedRate(...),然后在該任務完成時安排任務的下一次運行。
無論如何,當您安排一些事情時,您將得到回饋。
ScheduledFuture<?> handle = exec.scheduleAtFixedRate(...);
握住此把手以方便使用。 為了論證,我們假設它是TaskKey的地圖。 TaskKey是(群集| objectClass | ip地址|命令)一起作為對象。
Map<TaskKey,ScheduledFuture<?>> tasks = ...;
您可以使用該句柄來取消和安排新作業。
cancelForCustomer(CustomerId id) {
List<TaskKey> keys = db.findAllTasksOwnedByCustomer(id);
for(TaskKey key : keys) {
ScheduledFuture<?> f = tasks.get(key);
if(f!=null) f.cancel();
}
}
對於參數傳遞,創建一個對象來代表您的工作。 使用所需的所有參數創建其中之一。
class HostCheck implements Runnable {
private final Address host;
private final String command;
private final int something;
public HostCheck(Address host, String command; int something) {
this.host = host; this.command = command; this.something = something;
}
....
}
為了進行異常處理,請將所有內容本地化到您的對象中
class HostCheck implements Runnable {
...
public void run() {
try {
check();
scheduleNextRun(); // optionally, if fixed-rate doesn't work
} catch( Exception e ) {
db.markFailure(task); // or however.
// Point is tell somebody about the failure.
// You can use this to decide to stop scheduling checks for the host
// or whatever, but just record the info now and us it to influence
// future behavior in, er, the future.
}
}
}
好的,到目前為止,我認為我們的狀態還不錯。 需要填寫很多細節,但感覺很容易處理。 現在我們有了一些復雜性,那就是要求“ cluster / objectClass”對的執行是串行的。
有兩種方法可以解決此問題。
如果唯一對的數量很少,則可以使Map<ClusterObjectClassPair,ScheduledExecutorService>
,確保創建單線程執行器服務(例如Executors.newSingleThreadScheduledExecutor()
)。 因此,您沒有一堆調度服務(上面的exec
),而是一堆。 很簡單。
如果您需要控制同時嘗試的工作量,則可以讓每個HealthCheck在執行之前獲得許可。 有一些全球許可對象
public static final Semaphore permits = java.util.concurrent.Semaphore(30);
接着
class HostCheck implements Runnable {
...
public void run() {
permits.acquire()
try {
check();
scheduleNextRun();
} catch( Exception e ) {
// regular handling
} finally {
permits.release();
}
}
}
每個ClusterObjectClassPair
只具有一個線程,該線程可序列化該工作,然后僅限制ClusterObjectClassPair
可以交談的ClusterObjectClassPair
。
我想這使答案相當長。 祝好運。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.