关于 spring 定时任务的一个奇怪问题

2020-10-22 12:46:48 +08:00
 echooo0

项目是 springboot 写的
用 @Scheduled 写了一个 5 分钟执行一次的定时任务,用 @Async 来做了异步执行
任务开始的时候执行了一个获取系统名称的操作,就没有什么其他操作了

System.getProperty("os.name")

但是发现一个奇怪的问题,定时任务运行一段时间后,总是在晚上九点后就停止执行了
定时任务里面打了日志,也就是说在晚上 9:00 是正常执行完了的 ,但是下一个任务就不再开始执行了
重启 Tomcat 后又正常了

这个就感觉很奇怪。。。。

1946 次点击
所在节点    程序员
15 条回复
OysterQAQ
2020-10-22 12:51:01 +08:00
cron 默认只有一个线程,加上 async 后交给另一个固定的线程池,所以和 cron 没关系了,排查下那个线程池中各个线程的状态
echooo0
2020-10-22 13:02:47 +08:00
@OysterQAQ 对,是交给了另一个 SimpleAsyncTaskExecutor 的线程池,看日志里面,当时没有任何的报错,感觉不太好排查
OysterQAQ
2020-10-22 13:15:16 +08:00
@echooo0 可能有别的长时间执行的任务占用了线程池中所有的空闲线程 不过也有可能是别的 cron 任务占用了 cron 的线程 jstack 一下呗
OysterQAQ
2020-10-22 13:16:16 +08:00
报错是不可能报错的 如果队列大小和拒绝策略有设置的话,也没那么快报错
echooo0
2020-10-22 13:17:10 +08:00
@OysterQAQ 发现了一个问题,线程池的 id 非常大,755 这种

然后查了下,SimpleAsyncTaskExecutor,默认情况下,每次请求都会新开线程,不是真的线程池,这个类不重用线程。

感觉可能和这个有关系,但是开了这么多线程,也没 out of memory 错误报出来,只是 hang 住了,也是很奇怪。。。
OysterQAQ
2020-10-22 13:25:18 +08:00
@echooo0 哈,,我一般都是对这两个注解指定自己的线程池的 根据业务来新建线程池再在注解中指定使用哪个线程池 不至于互相干扰 我觉得没报错大多是因为根本就在队列中没执行 查查同样用了 cron 的有没有什么耗时很长的大任务
echooo0
2020-10-22 14:41:23 +08:00
@OysterQAQ 你这种 "根据业务来新建线程池再在注解中指定使用哪个线程池" , 可能控制的更精细化一些
我目前修改的办法,是实现了 AsyncConfigurer,定义了全局的线程池都用 ThreadPoolTaskExecutor 来做了
Vkery
2020-10-22 15:01:00 +08:00
接个 spring boot admin 看看线程状态
我以前遇到过也是定时任务过段时间就停了 后来发现好像是线程池用完了,因为有个定时任务,调用第三方的接口,因为没有超时机制,一直等待在那
PIECExx
2020-10-22 16:09:13 +08:00
@Scheduled 也可以配置多个并发线程数,也可以达到你说的这个效果。
如果配合 @Async 用,那你不得说下这个池子的配置么?
echooo0
2020-10-22 20:07:17 +08:00
@PIECExx 研究了下你说的,在 @Scheduled 里面控制并发数的时候,好像只能设置 corePoolSize,没法设置 maximumPoolSize

```java
public class ScheduleConfig implements SchedulingConfigurer {
private final Logger logger = LoggerFactory.getLogger(ScheduleConfig.class);


@Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
taskRegistrar.setScheduler(Executors.newScheduledThreadPool(80));
}
}

```
echooo0
2020-10-23 00:51:45 +08:00
搞定了,实现 ThreadPoolTaskScheduler 即可
xkzhangsan
2020-10-23 08:18:08 +08:00
自定义一个线程池比较好
echooo0
2020-10-23 11:31:33 +08:00
@PIECExx 试了下,好像如果直接去配置 @Scheduled 的并发,都没法设置 maximumPoolSize 这个参数,有点迷
不知道官方为什么有这么个逻辑
PIECExx
2020-10-23 13:42:51 +08:00
@echooo0 实现异步线程池就是交给另外一个池子做了,只要异步的那个池子没啥问题,任务也不会出错。
configureTasks 这个,你贴出来的 Executors.newScheduledThreadPool(80),是个 new 简版池子的方法。
源码里是 super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS, new DelayedWorkQueue());
maximumPoolSize 就是 Integer.MAX_VALUE 。
Executors 下面有 N 个方法可以 new 出来,你换个不就行了。。。。
echooo0
2020-10-24 00:47:09 +08:00
@PIECExx 对,源码那个我看了,一般规范的来说,maximumPoolSize 设置为 Integer.MAX_VALUE 不是不太好嘛

Executors 下面那些,好像只有 newScheduledThreadPool 适合的,taskRegistrar.setScheduler 对参数类型有限制

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/717417

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX