Running Only Once A Schedule Job Across Multiple Instances

喜欢而已 提交于 2021-01-29 07:33:52

问题


I have a schedule job that run every end of the month. After running it saves some data to database.

When i scale the app(for example with 2 instances) both instances run the schedule job and both save the data and at the end of day my database has the same data.

So i want the schedule job only run one time regardless of instances numbers at cloud.


回答1:


In my project, I have maintained a database table to hold a lock for each job which needs to be executed only once in the cluster. When a Job gets triggered then it first tries to acquire lock from the database and if it gets that lock only then it will get executed. If it fails to acquire the lock then job will not get executed.

You can also look at the clustering feature of Quartz job.

http://www.quartz-scheduler.org/documentation/2.4.0-SNAPSHOT/introduction.html




回答2:


I agree with the comments. If you can utilize a scheduler that's going to be your best, most flexible option. In addition, a scheduler should be executing your job as a "task" on Cloud Foundry. The task will only run on one instance, so you won't need to worry about how many instances your application is using (the two are separate in that regard).

If you're using Pivotal Cloud Foundry/Tanzu Cloud Foundry there is a scheduler you can ask your operations team to install. I don't know about other variants of CF, but I assume there are other schedulers.

https://network.pivotal.io/products/p-scheduler/


If using a scheduler is not an option then this is a concern you'll need to handle in your application. The solution of using a shared lock is a good one, but there is also a little trick you can do on Cloud Foundry that I feel is a little simpler.

When your application runs, certain environment variables are set by the platform. There is one called INSTANCE_INDEX which has a number indicating the instance on which the app is running. It's zero-based, so your first app instance will be running on instance zero, the second instance one, etc.

In your code, simply look at the instance index and see if it's zero. If the index is non-zero, have your task end without doing anything. If it's zero, then let the task proceed and do its work. The task will execute on every application instance, but it will only do work on the first instance. It's an easy way to guarantee something like a database migration or background job only runs once.


One final option would be to use multiple processes. This is a feature of Cloud Foundry that enables you to have different processes running, like your web process and a background worker process.

https://docs.cloudfoundry.org/devguide/multiple-processes.html

The interesting thing about this feature is that you can scale the different processes independently of each other. Thus you could have as many web processes running, but only one background worker which would guarantee that your background process only runs once.

That said, the downside of this approach is that you end up with separate containers for each process and the background process would need to continue running. The foundation expects it to be a long-running process, not a finite duration batch job. You could get around this by wrapping your periodic task a loop or something which keeps the process running forever.

I wouldn't really recommend this option but I wanted to throw it out there just in case.




回答3:


You can use @SnapLock annotation in your method which guarantees that task only runs once. See documentation in this repo https://github.com/luismpcosta/snap-scheduler

Example: Import maven dependency

<dependency>
  <groupId>io.opensw.scheduler</groupId>
  <artifactId>snap-scheduler-core</artifactId>
  <version>0.3.0</version>
</dependency>

After importing maven dependency, you'll need to create the required tables tables.

Finally, see bellow how to annotate methods which guarantees that only runs once with @SnapLock annotation:

import io.opensw.scheduler.core.annotations.SnapLock;
...

  @SnapLock(key = "UNIQUE_TASK_KEY", time = 60)
  @Scheduled(fixedRate = 30000)
  public void reportCurrentTime() {
    ...
  }

With this approach you also guarantee audit of the tasks execution.



来源:https://stackoverflow.com/questions/62994659/running-only-once-a-schedule-job-across-multiple-instances

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!