Java Spring @Scheduled tasks executing twice

前端 未结 23 1549
清歌不尽
清歌不尽 2020-11-29 09:07

I have a simple test method here that is set to run every 5 seconds and it does, but looking at the System.out you can see it appears to be doing something odd.



        
相关标签:
23条回答
  • 2020-11-29 09:46

    You might want to check if you are scanning components for the same package in two different contexts if your app is WEB, e.g. applicationContext.xml and then again some-servlet.xml.

    0 讨论(0)
  • 2020-11-29 09:46

    Had the same problem.

    In my case I moved my @Scheduled functions from @Service into new separate class with @Component annotation and it fixed my problem

    0 讨论(0)
  • 2020-11-29 09:51

    Hi,

    It could be because of any of the 2 reasons:

    1. App Context getting loaded twice, or
    2. You have multiple app instances running (for scaling purposes).

    For 1), you may follow any of the above answers. I will speak about (2).

    For (2), Generally, In cloud environments, you would have multiple instances (like 2,3,4....) running in cloud. Hence, your scheduler will be triggered by each app instance. I have faced this issue in cloud environment too. We run our app on Cloud Foundry and we have scaled to 2 instances of our app. Our scheduler started triggering twice (from each of the app instance i.e. app_instance_0 and app_instance_1).

    In order to overcome, we could do any one of the below:

    APPROACH I: Use System.getenv("CF_INSTANCE_INDEX");
    // this var is provided by default to the app by cloud foundry as env variable. it gives us the app instance index which triggered the method call. Hence, we can then use simple condition to alwys trigger schduler when app_instance_0 runs it using:

     @Scheduled(cron = * * * * * *)
     public void scheduledTask() {
     String appInstanceIndex = System.getenv("CF_INSTANCE_INDEX"); 
     if (appInstanceIndex.equals("0")) {
     .... //your scheduling logic here
     .... //your scheduling logic here
     }
     else {
     //dont do anything as its call from other app instances...
     } 
     }
                          
    

    One main advantage here is that we dont make any DB calls. Ofcourse it wont work if you dont use CF. But do check if your PaaS provides any such variable.

    APPROACH II: This is a genric approach. Implement a new table for scheduler. Add a unique constraint column. In your shceduler method, generate today's date and add it into Db. (this date is saved in table with column having unique constraint). Now, first time always this scheduler will run properly. When other app instances try to create todays date again and save into DB, it will lead to exception as DB has unique constraint and it cant add the date again. hence, for other app instances, the scheduler logic will fail and lead to exception.

     @Scheduled(cron = * * * * * *)
     public void scheduledTask() {
     String date = LocalDate.now().toString();
     try{
     insertDateInDb();
     // your scheduler logic here
     }
     catch(Exception e){
     // Dont do anything...
     }                    
     }
    
    0 讨论(0)
  • 2020-11-29 09:52

    Well in my case the job's bean had @Component annotation and i had this in my applicationContext.xml :

    <task:annotation-driven/> <bean id="getxxx" class="com.kitar.xxxx.jobs.RomeExample"></bean>

    So the solution is to delete the bean definition (the second line) because :

    <task:annotation-driven/>: allow the detection of @Async and @Scheduled annotations on any Spring-managed object so no need to define the job's bean or it will be called twice.

    0 讨论(0)
  • 2020-11-29 09:53

    I have faced the same situation and solved by this:

    1) Scheduler Service

    @Service
    public class SchedulerService {
    
        @Autowired
        @Qualifier("WorkerClass")
        private Worker worker;
    
        @Scheduled(cron="0/5 * * * * ?", zone="Asia/Colombo")//zone is a sample
        public void doSchedule() {
            worker.work();
        }
    
    }
    

    2) Worker Class

    @Component("WorkerClass")
    public class WorkerClass implements Worker {
    
        @Override
        public void work() {
            doSomething();
        }
    
        protected void doSomething() {
            system.out.pringln("What must I do?");
        }
    
    }
    
    0 讨论(0)
提交回复
热议问题