Java中的动态预定并发任务执行

时间:2014-05-09 20:20:48

标签: java multithreading concurrency quartz-scheduler

我正在尝试实现一个基于某些用户输入编程任务的应用程序。用户可以使用与之关联的telnet命令(一对一关系),执行频率和2组(cluster,objectClass)来放置大量IP。

用户应该能够在运行时添加/删除IP,集群,命令等。他们也应该能够中断执行。

此应用程序应该能够将telnet命令发送到IP,等待响应并根据频率将响应保存在数据库中。我遇到的问题是尝试将所有这些多线程化,因为至少有60,000个IP用于telnet,并且在单个线程中执行它将花费太多时间。一个线程应该使用相同的objectClass处理同一群集中的一组IP。

我看了Quartz来安排工作。使用Quartz,我尝试创建一个动态作业,其中包含IP列表(带有命令),处理它们并将结果保存到数据库。但后来我遇到了用户给出的不同计时器的问题。 Quartz网页上的示例不完整,不会过多详细说明。

然后我尝试使用旧的方式,使用java Threads,但我需要异常处理和参数传递,Threads不这样做。然后我发现了Callables和Executors,但我不能用Callables安排任务。

所以现在我很难过,我该怎么办?

1 个答案:

答案 0 :(得分:1)

好的,这是一些想法。带上必要的盐粒。

首先,创建一个需要执行的所有工作的列表。我假设你在表格中有这个,你可以创建一个如下所示的连接:

cluster | objectClass | ip-address | command | frequency | last-run-time

这代表了系统需要完成的所有工作。为了便于解释,我会说频率可以采取“每天1次”,“每小时1次”,“每小时4次”,“每分钟”的形式。该表每行有一行(cluster,objectClass,ip-address,command)。假设一个不同的表具有运行历史记录,包含错误消息和其他内容。

现在您需要做的是阅读该表,并安排工作。如需安排,请使用以下方法之一:

ScheduledExecutorService exec = Executors...

当你安排某些事情时,你需要告诉它经常运行的频率(我们给出的频率很容易)和延迟。如果每分钟运行一次并且最后一次运行4分30秒,则初始延迟为零。如果要每小时运行一次,则初始延迟为(60分钟 - 4.5分钟= 55.5分钟)。

ScheduledFuture<?> handle = exec.scheduleAtFixedRate(...);

更复杂的调度类型是为什么像Quartz这样的东西存在,但基本上你只需要一种方法来解决,给定(调度,最后运行)到下一次执行的经过时间。如果您可以这样做,那么您可以使用schedule(...)代替scheduleAtFixedRate(...),然后在该任务完成时安排下一次运行任务。

无论如何,当你安排某些事情时,你会得到一个回头的手柄

ScheduledFuture<?> handle = exec.scheduleAtFixedRate(...);

将此句柄放在可以访问的内容中。为了论证,让我们说它是TaskKey的地图。 TaskKey是(cluster | objectClass | ip-address |命令)作为对象。

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个。{/ p>

我想这让它变成了一个很长的答案。祝你好运。