spring boot确认在tomcat中cron定时器无法退出

```
@Component
public class TestScheduler {

	@Scheduled(cron = "0 0 4 * * ?")
	public void test() {
		system.out.println("hello, test...");
	}
	
	@Scheduled(fixedDelay=5000)
	public void test2() {
		System.out.println("tets....");
	}
}
```
上面是测试用的定时器,简单新建一个spring boot项目,然后加入上面的定时器代码是测试不出来的,因为默认spring boot下的定时器是单线程,而且在App退出时立即shutdown定时器线程,不等待线程执行结束。

```
@Configuration
public class SchedulerConfig implements SchedulingConfigurer {
	
	@Override
	public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
		taskRegistrar.setScheduler(taskExecutor());
	}
	
	@Bean(destroyMethod="shutdown", name="taskScheduler")
    public Executor taskExecutor() {
		ThreadPoolTaskScheduler scheduledExecutorService = new ThreadPoolTaskScheduler();
		scheduledExecutorService.setPoolSize(10);
		scheduledExecutorService.setWaitForTasksToCompleteOnShutdown(true);
		scheduledExecutorService.setAwaitTerminationSeconds(5);
		return scheduledExecutorService;
    }

}
```
改为使用线程池,并且设置等待TASK线程执行结束,设置最大等待时间,然后发布到tomcat后,就无法正常shutdown tomcat了,必须强制kill。其实在tomcat shutdown时,定时器线程池中的线程已经不再执行,只是不知为何无法退出,看到stackoverflow上也有人遇到类似问题,但没有人回答。经测试,如果去掉cron定时器就又可以正常shutdown。

目前产品业务上使用redis作为简单消息队列,如果直接强制kill tomcat进程,可能导致redis队列的数据丢失,目前部署使用的脚本是等待20s,如果进程还在,则强制kill,这样可以尽可能的保证在kill之前,定时器的线程执行结束。

其实这里如果使用kafka、activeMQ之类的消息队列产品就没问题了,因为这些产品都支持事务处理,如果在commit之前强制kill,这些数据就还在队列中并不会丢失。不过,目前产品部署在AWS中,AWS自身没有kafka,自建的话由于产品量级不到又显得多余,AWS有类似kafka的kinesis,但是本地开发没有环境,麻烦。

所以,目前正在整理redis的另一个方案,消息队列的安全模式,下次再写。
赞 (0) 评论 分享 ()