繁体   English   中英

如果Spring Scheduled执行在某个固定时间后挂起,则停止

[英]stop Spring Scheduled execution if it hangs after some fixed time

我已经使用Spring Framework的Scheduled计划了我的工作,以使用cron每5分钟运行一次。 但是有时候我的工作会无限期地等待外部资源,因此我无法在那儿超时。 我无法使用fixedDelay因为以前的进程有时会进入无限等待模式,因此我必须每5分钟刷新一次数据。

所以我一直在寻找Spring Framework的Scheduled任何选项,以便在fixed-time运行成功或失败之后停止该进程/线程。

我发现以下设置将我在@Configuration类中放置的keepAliveTime用120秒初始化ThreadPoolExecutor设置。 谁能告诉我这项工作会按我预期的那样进行。

@Bean(destroyMethod="shutdown")
public Executor taskExecutor() {
    int coreThreads = 8;
    int maxThreads = 20;
    final ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
            coreThreads, maxThreads, 120L, 
            TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>()
    );
    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<Object> result = doSomething();
        result.get(timeout, TimeUnit.MILLISECONDS);
    }

    @Async
    Future<Object> doSomething() {
        //what i should do
        //get ressources etc...
    }
}

不要忘记添加@EnableAsync

通过实现Callable,也可以在没有@Async的情况下执行相同的@Async

编辑:请记住,它将等待直到超时,但是运行任务的线程不会被中断。 发生TimeoutException时,您将需要调用Future.cancel。 并在任务中检查isInterrupted()以停止处理。 如果要调用api,请确保已选中isInterrupted()。

allowCoreThreadTimeOuttimeout设置无济于事,因为它只允许工作线程在一段时间没有工作后结束(请参阅javadocs)

您说您的工作无限期地等待外部资源。 我敢肯定这是因为您(或您使用的某些第三方库)使用默认情况下无限超时的套接字。 还请记住,当jvm在socket.connect / read上阻塞时,它会忽略Thread.interrupt()的内容。

因此,找出您的任务中使用的女巫套接字库(及其使用方式),并更改其默认超时设置。

例如:在Spring内部广泛使用RestTemplate (在Rest客户端,Spring社交中,Spring Security OAuth等中)。 并且有ClientHttpRequestFactory实现来创建RestTemplate实例。 默认情况下,spring使用使用JDK套接字的SimpleClientHttpRequestFactory 默认情况下,所有超时都是无限的。

因此,找出冻结的确切位置,阅读文档并正确配置。

PS:如果您没有足够的时间并且感到“幸运”,请尝试通过将jvm属性sun.net.client.defaultConnectTimeoutsun.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
        }
    }
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM