使用 ExecutorService 的时间限制任务
Time bound tasks using ExectutorService
我早就运行ning任务提交了一个ExecutorService
。该任务可以 运行 相当长一段时间。同时,新的任务被提交到内部阻塞队列。
提交的任务完成后,会发回通知以从队列中释放任务以供执行。但是,有时,由于编程错误或网络问题,通知不会被触发。在这种情况下,我的任务队列有可能会变得非常大,我可能会遇到任务可能永远躺在队列中的情况。
为了克服这个问题,我正在考虑编写一个线程,它会定期检查任务在队列中空闲的时间。如果任务在队列中等待了 15 分钟,我会假设较早提交的任务遇到了错误,因此没有 return。然后我将从队列中逐出任务并允许它执行。
是否有任何现有机制来处理这个问题,或者我将不得不编写这个自定义逻辑?
注:
我不喜欢 ScheduledExecutor
服务的原因是因为并非所有任务都需要定期执行。只有失败的场景应该在一定的延迟后执行。
编辑
架构简要概述
我正在设计的解决方案应该支持许多并发的静态文件下载。通常可能有数千个下载请求。下载请求由基于 UI 的应用程序触发。这样我就知道什么时候会触发请求。利用这种方法,我打算限制下载请求。
如果用户创建了 300 个下载请求,会发生什么情况?
- 应用程序工作线程创建 300 个下载任务
- 100 个任务是 submitted.I 定义的最大 HTTP 线程池大小为 100。这意味着我可以支持最多 100 个同步并行下载(servlet 2.5)任务反过来要求远程 HTTP 客户端执行 HTTP 获取。请注意,HTTP 线程尚未发挥作用
- 剩余 200 个请求正在排队。
- HTTP 客户端执行 HTTP Get。 HTTP 线程现在以阻塞方式流式传输响应。
- 收到 200 OK 后,我会创建一个通知,通知其中一个客户端已完成下载。
- 限制现在 release/submit 之前排队的 200 个请求中的任务之一。
在我能够收到响应(HTTP 200/HTTP 500 等)的情况下,节流机制非常有效。但是,例如,如果 servlet 本身抛出异常,我没有收到任何响应以指示 HTTP 工作线程空闲。因此任务有可能永远保留在队列中。为了克服这个问题,我在考虑一种基于计时器的方法,如果 15 分钟内没有 HTTP 响应,则提交下一个队列任务以供执行。一种避免重大内存泄漏的回退机制。
通过超时调用 get()
来限制任务允许的最长时间,并在捕获 TimeoutException
时执行清理。
这是一个在等待时不会阻塞主线程的实现:
ExecutorService executor = Executors.newCachedThreadPool();
ExecutorService monitor = Executors.newFixedThreadPool(99);
public void submit(Runnable task) {
Runnable monitorTask = new Runnable() {
@Override
public void run() {
Future<?> future = executor.submit(task);
try {
future.get(15, TimeUnit.MINUTES);
} catch (InterruptedException e) {
// retry waiting. iterative approach not shown here
} catch (ExecutionException e) {
// your task exploded
} catch (TimeoutException e) {
// your task timed out - perform clean up, eg
future.cancel(true);
}
}
};
monitor.submit(monitorTask);
}
单独的线程池用于防止没有线程可用于监视,但线程可用于执行导致未监视任务的任务。
我早就运行ning任务提交了一个ExecutorService
。该任务可以 运行 相当长一段时间。同时,新的任务被提交到内部阻塞队列。
提交的任务完成后,会发回通知以从队列中释放任务以供执行。但是,有时,由于编程错误或网络问题,通知不会被触发。在这种情况下,我的任务队列有可能会变得非常大,我可能会遇到任务可能永远躺在队列中的情况。
为了克服这个问题,我正在考虑编写一个线程,它会定期检查任务在队列中空闲的时间。如果任务在队列中等待了 15 分钟,我会假设较早提交的任务遇到了错误,因此没有 return。然后我将从队列中逐出任务并允许它执行。
是否有任何现有机制来处理这个问题,或者我将不得不编写这个自定义逻辑?
注:
我不喜欢 ScheduledExecutor
服务的原因是因为并非所有任务都需要定期执行。只有失败的场景应该在一定的延迟后执行。
编辑 架构简要概述 我正在设计的解决方案应该支持许多并发的静态文件下载。通常可能有数千个下载请求。下载请求由基于 UI 的应用程序触发。这样我就知道什么时候会触发请求。利用这种方法,我打算限制下载请求。
如果用户创建了 300 个下载请求,会发生什么情况?
- 应用程序工作线程创建 300 个下载任务
- 100 个任务是 submitted.I 定义的最大 HTTP 线程池大小为 100。这意味着我可以支持最多 100 个同步并行下载(servlet 2.5)任务反过来要求远程 HTTP 客户端执行 HTTP 获取。请注意,HTTP 线程尚未发挥作用
- 剩余 200 个请求正在排队。
- HTTP 客户端执行 HTTP Get。 HTTP 线程现在以阻塞方式流式传输响应。
- 收到 200 OK 后,我会创建一个通知,通知其中一个客户端已完成下载。
- 限制现在 release/submit 之前排队的 200 个请求中的任务之一。
在我能够收到响应(HTTP 200/HTTP 500 等)的情况下,节流机制非常有效。但是,例如,如果 servlet 本身抛出异常,我没有收到任何响应以指示 HTTP 工作线程空闲。因此任务有可能永远保留在队列中。为了克服这个问题,我在考虑一种基于计时器的方法,如果 15 分钟内没有 HTTP 响应,则提交下一个队列任务以供执行。一种避免重大内存泄漏的回退机制。
通过超时调用 get()
来限制任务允许的最长时间,并在捕获 TimeoutException
时执行清理。
这是一个在等待时不会阻塞主线程的实现:
ExecutorService executor = Executors.newCachedThreadPool();
ExecutorService monitor = Executors.newFixedThreadPool(99);
public void submit(Runnable task) {
Runnable monitorTask = new Runnable() {
@Override
public void run() {
Future<?> future = executor.submit(task);
try {
future.get(15, TimeUnit.MINUTES);
} catch (InterruptedException e) {
// retry waiting. iterative approach not shown here
} catch (ExecutionException e) {
// your task exploded
} catch (TimeoutException e) {
// your task timed out - perform clean up, eg
future.cancel(true);
}
}
};
monitor.submit(monitorTask);
}
单独的线程池用于防止没有线程可用于监视,但线程可用于执行导致未监视任务的任务。