装饰 Java 的可调用以添加行为
Decorating Java's callable to add behavior
假设我们有潜在的长运行宁任务:
public class LongRunningTask {
public ReturnType doSomething() {
...
}
}
并且我们希望 运行 同时执行许多这些任务。
所以,我有我的要求:
public class LongRunningCallable implements Callable<LongRunningTask> {
private final LongRunningTask task;
...
public ReturnType call() {
return task.doSomething();
}
...
}
现在,由于这可能真的会持续很长时间,我可能想将其限制为 运行 仅用于特定数量。所以,我可能会这样做:
public class InterruptibleCallable<T> implements Callable<T> {
protected final long timeout;
protected final TimeUnit timeUnit;
protected final Callable<T> callable;
public InterruptibleCallable(long timeout, TimeUnit timeUnit, Callable<T> callable) {
this.timeout = timeout;
this.timeUnit = timeUnit;
this.callable = callable;
}
@Override
public T call() throws Exception {
ExecutorService executorService = Executors.newSingleThreadExecutor();
T result = null;
try {
result = executorService.submit(callable).get(timeout, timeUnit);
} catch (InterruptedException e) {
LOGGER.error("Callable: " + callable.toString() + " was interrupted");
throw e;
} catch (ExecutionException e) {
throw e;
} catch (TimeoutException e) {
LOGGER.warn("Callable: " + callable.toString() + " timed out after " + timeout + " " + timeUnit);
throw e;
} finally {
executorService.shutdown();
}
return result;
}
}
没关系,但现在我还想包装它,以便它可以在遇到异常时重试(延迟):
public class RetryableCallable<T> implements Callable<T> {
private final long delay;
private final TimeUnit timeUnit;
private final Callable<T> callable;
@Override
public T call() throws Exception {
T result = null;
try {
ExecutorService executorService = Executors.newSingleThreadExecutor();
result = executorService.submit(this.callable).get();
} catch (Exception e) {
ScheduledExecutorService executorService = Executors.newSingleThreadScheduledExecutor();
result = executorService.schedule(this.callable, delay, timeUnit).get();
}
return result;
}
}
现在,我的问题:
是否已经有提供此功能(或超集)的库?
这是一个好的设计吗,尤其是创建另一个执行器并在每个包装器中提交一个可调用项,为什么?
从设计模式和性能的角度来看,解决这个问题的最佳方法是什么?
谢谢 :D
将 RetryableCallable
包裹在 InterruptibleCallable
周围,包裹 LongRunningTask
并在每次执行时创建两个额外的执行程序 RetryableCallable
执行是错误的。
当 RetryableCallable
捕获 TimeoutException
通常它不应该再次 运行 相同的任务。这有点令人困惑,因为如果任务超时 "killed" 为什么要再 运行 一次?
另外为什么需要在这里创建另一个执行器?保持简单
public class RetryableCallable<T> implements Callable<T> {
private int retries = 3;
@Override
public T call() throws Exception {
while (retries-- > 0) {
try {
return callable.call();
} catch (InterruptedException e) {
LOGGER.error("Callable: " + callable.toString() + " was interrupted");
throw e;
} catch (TimeoutException e) {
LOGGER.warn("Callable: " + callable.toString() + " timed out after " + timeout + " " + timeUnit);
throw e;
} catch (Exception e) {
LOGGER.warn("Callable: " + callable.toString() + " failed");
}
}
throw new IllegalStateException();//or return null
}
}
它不应该使用超时和延迟,它是 RetryableCallable
而不是 RetryableCallableWithDelayAndTimeoutAndSomethingElse
如果你想限制任务的执行时间,我看到至少有 4 个好的方法:
- 在提交它的线程中调用
get(time, timeunit)
- 限制任务在
call()
函数内的执行时间,例如通过检查 "periodically" 我们还有更多时间吗。
- 制作你自己的执行器class,其中包含自定义执行器和一个线程审计器,它会接受一些
TimeLimitedCallable
扩展 Callable
并具有 int getTimeLimit()
功能。审计员将控制实施 TimeLimitedCallable
. 的所有 运行ning 任务的时间范围
- 为 "task's auditors" 创建一个单独的执行器,并在将一个(或一堆)具有业务逻辑的任务提交到主执行器后 - 创建审计任务并将其提交到单独的执行器中。
假设我们有潜在的长运行宁任务:
public class LongRunningTask {
public ReturnType doSomething() {
...
}
}
并且我们希望 运行 同时执行许多这些任务。
所以,我有我的要求:
public class LongRunningCallable implements Callable<LongRunningTask> {
private final LongRunningTask task;
...
public ReturnType call() {
return task.doSomething();
}
...
}
现在,由于这可能真的会持续很长时间,我可能想将其限制为 运行 仅用于特定数量。所以,我可能会这样做:
public class InterruptibleCallable<T> implements Callable<T> {
protected final long timeout;
protected final TimeUnit timeUnit;
protected final Callable<T> callable;
public InterruptibleCallable(long timeout, TimeUnit timeUnit, Callable<T> callable) {
this.timeout = timeout;
this.timeUnit = timeUnit;
this.callable = callable;
}
@Override
public T call() throws Exception {
ExecutorService executorService = Executors.newSingleThreadExecutor();
T result = null;
try {
result = executorService.submit(callable).get(timeout, timeUnit);
} catch (InterruptedException e) {
LOGGER.error("Callable: " + callable.toString() + " was interrupted");
throw e;
} catch (ExecutionException e) {
throw e;
} catch (TimeoutException e) {
LOGGER.warn("Callable: " + callable.toString() + " timed out after " + timeout + " " + timeUnit);
throw e;
} finally {
executorService.shutdown();
}
return result;
}
}
没关系,但现在我还想包装它,以便它可以在遇到异常时重试(延迟):
public class RetryableCallable<T> implements Callable<T> {
private final long delay;
private final TimeUnit timeUnit;
private final Callable<T> callable;
@Override
public T call() throws Exception {
T result = null;
try {
ExecutorService executorService = Executors.newSingleThreadExecutor();
result = executorService.submit(this.callable).get();
} catch (Exception e) {
ScheduledExecutorService executorService = Executors.newSingleThreadScheduledExecutor();
result = executorService.schedule(this.callable, delay, timeUnit).get();
}
return result;
}
}
现在,我的问题:
是否已经有提供此功能(或超集)的库?
这是一个好的设计吗,尤其是创建另一个执行器并在每个包装器中提交一个可调用项,为什么?
从设计模式和性能的角度来看,解决这个问题的最佳方法是什么?
谢谢 :D
将 RetryableCallable
包裹在 InterruptibleCallable
周围,包裹 LongRunningTask
并在每次执行时创建两个额外的执行程序 RetryableCallable
执行是错误的。
当 RetryableCallable
捕获 TimeoutException
通常它不应该再次 运行 相同的任务。这有点令人困惑,因为如果任务超时 "killed" 为什么要再 运行 一次?
另外为什么需要在这里创建另一个执行器?保持简单
public class RetryableCallable<T> implements Callable<T> {
private int retries = 3;
@Override
public T call() throws Exception {
while (retries-- > 0) {
try {
return callable.call();
} catch (InterruptedException e) {
LOGGER.error("Callable: " + callable.toString() + " was interrupted");
throw e;
} catch (TimeoutException e) {
LOGGER.warn("Callable: " + callable.toString() + " timed out after " + timeout + " " + timeUnit);
throw e;
} catch (Exception e) {
LOGGER.warn("Callable: " + callable.toString() + " failed");
}
}
throw new IllegalStateException();//or return null
}
}
它不应该使用超时和延迟,它是 RetryableCallable
而不是 RetryableCallableWithDelayAndTimeoutAndSomethingElse
如果你想限制任务的执行时间,我看到至少有 4 个好的方法:
- 在提交它的线程中调用
get(time, timeunit)
- 限制任务在
call()
函数内的执行时间,例如通过检查 "periodically" 我们还有更多时间吗。 - 制作你自己的执行器class,其中包含自定义执行器和一个线程审计器,它会接受一些
TimeLimitedCallable
扩展Callable
并具有int getTimeLimit()
功能。审计员将控制实施TimeLimitedCallable
. 的所有 运行ning 任务的时间范围
- 为 "task's auditors" 创建一个单独的执行器,并在将一个(或一堆)具有业务逻辑的任务提交到主执行器后 - 创建审计任务并将其提交到单独的执行器中。