我正在编写一个具有cron作业的应用程序,该作业每60秒执行一次。该应用程序被配置为在需要时扩展到多个实例。我只想每60秒(在任何节点上)在1个实例上执行任务。开箱即用,我找不到解决方案,但令我惊讶的是,之前没有多次被问到。我正在使用Spring 4.1.6。
<task:scheduled-tasks> <task:scheduled ref="beanName" method="execute" cron="0/60 * * * * *"/> </task:scheduled-tasks>
批处理和计划的作业通常在自己的独立服务器上运行,而不是面向客户的应用程序,因此将作业包含在预期在群集中运行的应用程序中并不是普遍的要求。此外,群集环境中的作业通常不需要担心同一作业的其他实例并行运行,因此隔离作业实例的要求不是很大。
一个简单的解决方案是在Spring Profile中配置你的作业。例如,如果你当前的配置是:
<beans> <bean id="someBean" .../> <task:scheduled-tasks> <task:scheduled ref="someBean" method="execute" cron="0/60 * * * * *"/> </task:scheduled-tasks> </beans>
更改为:
<beans> <beans profile="scheduled"> <bean id="someBean" .../> <task:scheduled-tasks> <task:scheduled ref="someBean" method="execute" cron="0/60 * * * * *"/> </task:scheduled-tasks> </beans> </beans>
然后,仅在scheduled激活了配置文件(-Dspring.profiles.active=scheduled)的一台计算机上启动应用程序。
scheduled
-Dspring.profiles.active=scheduled
如果主服务器由于某种原因不可用,只需启动另一个启用了配置文件的服务器,一切就可以继续正常工作。
如果你还想为作业进行自动故障转移,那么事情将会改变。然后,你将需要使作业保持在所有服务器上运行,并通过公用资源(例如数据库表,集群缓存,JMX变量等)检查同步。
确实有一个ShedLock项目可以满足此目的。你只需注释执行时应锁定的任务
@Scheduled( ... ) @SchedulerLock(name = "scheduledTaskName") public void scheduledTask() { // do something }
配置Spring和LockProvider
@Configuration @EnableScheduling @EnableSchedulerLock(defaultLockAtMostFor = "30s") class MySpringConfiguration { ... @Bean public LockProvider lockProvider(DataSource dataSource) { return new JdbcTemplateLockProvider(dataSource); } ... }