简体   繁体   English

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

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

I have used Spring Framework's Scheduled to schedule my job to run at every 5 mins using cron. 我已经使用Spring Framework的Scheduled计划了我的工作,以使用cron每5分钟运行一次。 But sometime my job waits infinitely for an external resource and I can't put timeout there. 但是有时候我的工作会无限期地等待外部资源,因此我无法在那儿超时。 I can't use fixedDelay as previous process sometime goes in wait infinitely mode and I have to refresh data at every 5 mins. 我无法使用fixedDelay因为以前的进程有时会进入无限等待模式,因此我必须每5分钟刷新一次数据。

So I was looking any option in Spring Framework's Scheduled to stop that process/thread after a fixed-time either it run successfully or not. 所以我一直在寻找Spring Framework的Scheduled任何选项,以便在fixed-time运行成功或失败之后停止该进程/线程。

I have found below setting which initialized ThreadPoolExecutor with 120 seconds for keepAliveTime which I put in @Configuration class. 我发现以下设置将我在@Configuration类中放置的keepAliveTime用120秒初始化ThreadPoolExecutor设置。 Can anybody tell me will this work as I expected. 谁能告诉我这项工作会按我预期的那样进行。

@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;
}

I'm not sure this will work as expected. 我不确定这是否会按预期工作。 Indeed the keepAlive is for IDLE thread and I don't know if your thread waiting for resources is in IDLE. 实际上,keepAlive是用于IDLE线程的,并且我不知道您等待资源的线程是否在IDLE中。 Furthermore it's only when the number of threads is greater than the core so you can't really know when it happen unless you monitor the threadpool. 此外,仅当线程数大于内核数时,除非监视线程池,否则您无法真正知道何时发生。

keepAliveTime - when the number of threads is greater than the core, this is the maximum time that excess idle threads will wait for new tasks before terminating. keepAliveTime-当线程数大于内核数时,这是多余的空闲线程将在终止之前等待新任务的最长时间。

What you can do is the following: 您可以执行以下操作:

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...
    }
}

Don't forget to add @EnableAsync 不要忘记添加@EnableAsync

It's also possible to do the same without @Async by implementing a Callable. 通过实现Callable,也可以在没有@Async的情况下执行相同的@Async

Edit: Keep in mind that it will wait until timeout but the thread running the task won't be interrupted. 编辑:请记住,它将等待直到超时,但是运行任务的线程不会被中断。 You will need to call Future.cancel when TimeoutException occurs. 发生TimeoutException时,您将需要调用Future.cancel。 And in the task check for isInterrupted() to stop the processing. 并在任务中检查isInterrupted()以停止处理。 If you are calling an api be sure that isInterrupted() is checked. 如果要调用api,请确保已选中isInterrupted()。

allowCoreThreadTimeOut and timeout setting doesn't help cause it just allow work thread to be ended after some time without work (See javadocs) allowCoreThreadTimeOuttimeout设置无济于事,因为它只允许工作线程在一段时间没有工作后结束(请参阅javadocs)

You say your job waits infinitely for an external resource. 您说您的工作无限期地等待外部资源。 I'am sure it's because you (or some third-party library you using) use sockets with time out infinite-by-default. 我敢肯定这是因为您(或您使用的某些第三方库)使用默认情况下无限超时的套接字。 Also keep in mind what jvm ignores Thread.interrupt() when it blocked on socket.connect/read. 还请记住,当jvm在socket.connect / read上阻塞时,它会忽略Thread.interrupt()的内容。

So find out witch socket library used in your task (and how exactly it used) and change it's default timeout settings. 因此,找出您的任务中使用的女巫套接字库(及其使用方式),并更改其默认超时设置。

As example: there is RestTemplate widely used inside Spring (in rest client, in spring social, in spring security OAuth and so on). 例如:在Spring内部广泛使用RestTemplate (在Rest客户端,Spring社交中,Spring Security OAuth等中)。 And there is ClientHttpRequestFactory implementation to create RestTemplate instances. 并且有ClientHttpRequestFactory实现来创建RestTemplate实例。 By default, spring use SimpleClientHttpRequestFactory which use JDK sockets. 默认情况下,spring使用使用JDK套接字的SimpleClientHttpRequestFactory And by default all it's timeouts are infinite. 默认情况下,所有超时都是无限的。

So find out where exactly you freeze, read it's docs and configure it properly. 因此,找出冻结的确切位置,阅读文档并正确配置。

PS If you don't have enough time and "feeling lucky" try to run your app with setting jvm properties sun.net.client.defaultConnectTimeout and sun.net.client.defaultReadTimeout to some reasonable values (See docs for more details) PS:如果您没有足够的时间并且感到“幸运”,请尝试通过将jvm属性sun.net.client.defaultConnectTimeoutsun.net.client.defaultReadTimeout设置为一些合理的值来运行您的应用程序(有关更多详细信息,请参阅文档

The keepAliveTime is just for cleaning out worker threads that hasn't been needed for a while - it doesn't have any impact on the execution time of the tasks submitted to the executor. keepAliveTime仅用于清除一段时间以来不需要的工作线程-它对提交给执行程序的任务的执行时间没有任何影响。

If whatever is taking time respects interrupts you can start a new thread and join it with a timeout, interrupting it if it doesn't complete in time. 如果花费时间考虑中断,您可以启动一个新线程并以超时将其加入,如果未及时完成,则将其中断。

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