停止Spring计划执行,如果它在一段固定时间后挂起
我使用Spring Framework的Scheduled
来安排我的工作使用cron每5分钟运行一次。 但有时我的工作无限期地等待外部资源,我不能把超时放在那里。 我不能使用fixedDelay
作为先前的进程有时进入等待无限模式,我必须每隔5分钟刷新一次数据。
因此,我在Spring Framework的Scheduled
寻找任何选项,在fixed-time
之后停止该进程/线程,无论它是否成功运行。
我发现下面的设置初始化ThreadPoolExecutor
为120秒, keepAliveTime
放在@Configuration
类中。 任何人都能告诉我这项工作是否符合我的预期。
@Bean(destroyMethod="shutdown") public Executor taskExecutor() { int coreThreads = 8; int maxThreads = 20; final ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor( coreThreads, maxThreads, 120L, TimeUnit.SECONDS, new LinkedBlockingQueue() ); threadPoolExecutor.allowCoreThreadTimeOut(true); return threadPoolExecutor; }
我不确定这会按预期工作。 确实keepAlive用于IDLE线程,我不知道你的线程在等待资源是否处于IDLE状态。 此外,只有当线程数大于核心时,除非您监视线程池,否则您无法确切知道它何时发生。
keepAliveTime – 当线程数大于核心时,这是多余空闲线程在终止之前等待新任务的最长时间。
你能做的是以下几点:
public class MyTask { private final long timeout; public MyTask(long timeout) { this.timeout = timeout; } @Scheduled(cron = "") public void cronTask() { Future
不要忘记添加@EnableAsync
通过实现Callable,也可以在没有@Async的情况下执行相同的操作。
编辑:请记住,它将等到超时,但运行任务的线程不会被中断。 发生TimeoutException时,您需要调用Future.cancel。 并在任务检查isInterrupted()以停止处理。 如果您正在调用api,请确保选中isInterrupted()。
allowCoreThreadTimeOut和timeout设置没有帮助导致它只允许工作线程在一段时间后无法工作(参见javadocs)
你说你的工作无限期地等待外部资源。 我确定这是因为您(或您使用的某些第三方库)使用的套接字默认超时。 还要记住,当它在socket.connect / read上被阻塞时,jvm会忽略Thread.interrupt()。
因此,找出您的任务中使用的女巫套接字库(以及它是如何使用的)并更改它的默认超时设置。
例如: RestTemplate在Spring中广泛使用(在rest客户端,春季社交,春季安全OAuth等)。 并且有ClientHttpRequestFactory实现来创建RestTemplate实例。 默认情况下,spring使用SimpleClientHttpRequestFactory ,它使用JDK套接字。 默认情况下,所有超时都是无限的。
因此,找出你准确冻结的位置,阅读它的文档并正确配置它。
PS如果你没有足够的时间和“感觉幸运”尝试运行你的应用程序将jvm属性sun.net.client.defaultConnectTimeout和sun.net.client.defaultReadTimeout设置为一些合理的值(有关更多详细信息,请参阅文档 )
keepAliveTime
仅用于清除一段时间内不需要的工作线程 – 它对提交给执行程序的任务的执行时间没有任何影响。
如果花费时间考虑中断,您可以启动一个新线程并加入超时,如果没有及时完成则中断它。
public class SomeService { @Scheduled(fixedRate = 5 * 60 * 1000) public void doSomething() throws InterruptedException { Thread taskThread = new TaskThread(); taskThread.start(); taskThread.join(120 * 000); if(taskThread.isAlive()) { // We timed out taskThread.interrupt(); } } private class TaskThread extends Thread { public void run() { // Do the actual work here } } }