具有重试和故障转移的Java线程/进程

时间:2013-05-29 14:23:10

标签: java multithreading rpc

我正在寻求在Java中实现一个我生成任务的机制。该任务的作用实际上与此无关,可以通过线程或新进程完成。

我真正需要的是:

  • 任务可以设置为重试N次
  • 如果任务中发生了令人讨厌的事情,那么产生它的父母就会受到保护
  • 以上两点都应该从我身上抽出来

有没有人知道在Java中可以做到的任何框架?

3 个答案:

答案 0 :(得分:2)

我一直在思考这个问题已经有一段时间了,最​​后得到了你认为有用的东西。

我们的小“框架”的第一部分是Task界面:

public interface Task {
    void work();
    void stop();
}

实现这一点,我们可以设置WorkTask

public class WorkTask implements Task {
    @Override
    public void work() {
        // Do something useful...
        // might throw SomethingNastyHappenedException
    }

    @Override
    public void stop() {
        // implement as needed e.g. to break inner loops
    }

}

为了符合您的要求,我们可以创建一个Wrapper类:

public class RetryingTaskWrapper implements Task {
    private Task wrappedTask;
    private int maxTries;
    private boolean running = false;

    public RetryingTaskWrapper(Task wrappedTask, int maxTries) {
        this.wrappedTask = wrappedTask;
        this.maxTries = maxTries;
    }

    @Override
    public void work() {
        running = true;
        boolean success = false;
        int tries = 0;

        while (running && !success && tries < maxTries) {
            try {
                wrappedTask.work();
                success = true;
            } catch (SomethingNastyHappenedException e) {
                // something nasty happed so retry
                tries++;
            } catch (Exception e) {
                // something even worse happened -> end
                running = false;
            }
        }

        running = false;
    }

    @Override
    public void stop() {
        running = false;
        wrappedTask.stop();
    }
}

最后我们需要一些东西来运行我们的任务......

public class TaskRunner implements Runnable {
    private Thread thread;
    private Task task;
    private boolean running = false;

    public TaskRunner(Task task) {
        this.task = task;
    }

    public synchronized void start() {
        if (thread != null) {
            return;
        }

        thread = new Thread(this);
        thread.start();
    }

    @Override
    public void run() {
        running = true;

        if (task != null) {
            try {
                task.work();
            } catch (Exception e) {
                // log your exception
            }
        }

        thread = null;
        running = false;
    }

    public void stop() {
        if (running) {
            if (task != null) {
                task.stop();
            }
            if (thread != null) {
                thread.interrupt();
            }
        }
    }
}

现在我们需要的是设置一个TaskRunner并启动它:

WorkTask workTask = new WorkTask();
TaskRunner runner = new TaskRunner(new RetryingTaskWrapper(workTask, 5));
runner.start();

如果您只想运行WorkTask一次而不进行重试,那么:

WorkTask workTask = new WorkTask();
TaskRunner runner = new TaskRunner(workTask);
runner.start();

如果您愿意,您甚至可以在无限循环中运行WorkTask,而无需更改WorkTask本身。您所需要的只是创建另一个Wrapper

public class InfiniteLoopTaskWrapper implements Task {
    private Task wrappedTask;
    private boolean running = false;

    public InfiniteLoopTaskWrapper(Task wrappedTask) {
        this.wrappedTask = wrappedTask;
    }

    @Override
    public void work() {
        running = true;

        while (running) {
            try {
                wrappedTask.work();
            } catch (SomethingNastyHappenedException e) {
                // log that something nasty happened
            } catch (Exception e) {
                // something even worse happened -> end
                running = false;
            }
        }
    }

    @Override
    public void stop() {
        running = false;
        wrappedTask.stop();
    }

}

并运行它......

WorkTask workTask = new WorkTask();
TaskRunner runner = new TaskRunner(new InfiniteLoopTaskWrapper(workTask));
runner.start();

答案 1 :(得分:2)

Akka非常适合这个问题。

或者,如果您只是想在不使用Akka / actors的情况下更好地进行故障处理,您可以尝试像Sarge那样支持重试和故障升级的方式,类似于Akka的监督。

答案 2 :(得分:1)

  

如果任务中发生了令人讨厌的事情,那么产生它的父母就会受到保护

所以我猜你有3个选择。

  1. 是在另一个进程中运行任务。见Get Java Runtime Process running in background之类的内容。我不知道一个有助于此的框架。这个框架可以为你节省太多代码。

  2. 在同一JVM上的另一个线程中运行。见下文。

  3. 在另一台服务器上运行另一个进程。您将不得不编写客户端/服务器或使用Web框架或RMI进行远程处理。我不知道任何框架会自动为你做这件事。

  4. 就在同一个JVM中运行它而言,我根本不使用框架。我只是在循环中运行任务并进行适当的异常处理:

    for (int i = 0; i < retries; i++) {
       try {
          doTask();
       } catch (Throwable t) {
          // log the throwable here
       }
    }
    

    似乎框架太复杂了。如果任务内存不足,那么在另一个线程中运行它将无济于事,您将不得不生成另一个进程来处理该任务。

    如果你需要它在后台运行,那么@assyliaas关于使用线程池的初步答案是正确的方法。