V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
echooo0
V2EX  ›  程序员

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

  •  
  •   echooo0 · 2020-10-22 12:46:48 +08:00 · 2099 次点击
    这是一个创建于 1498 天前的主题,其中的信息可能已经有所发展或是发生改变。

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

    System.getProperty("os.name")
    

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

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

    第 1 条附言  ·  2020-10-22 16:46:38 +08:00
    目前看起来应该是 Async 注解 默认使用的线程池 SimpleAsyncTaskExecutor 无限创建新线程导致的问题
    已经自定义了 AsyncConfigurer, 使用 ThreadPoolTaskExecutor 线程池,后续看看效果
    15 条回复    2020-10-24 00:47:09 +08:00
    OysterQAQ
        1
    OysterQAQ  
       2020-10-22 12:51:01 +08:00 via iPhone
    cron 默认只有一个线程,加上 async 后交给另一个固定的线程池,所以和 cron 没关系了,排查下那个线程池中各个线程的状态
    echooo0
        2
    echooo0  
    OP
       2020-10-22 13:02:47 +08:00
    @OysterQAQ 对,是交给了另一个 SimpleAsyncTaskExecutor 的线程池,看日志里面,当时没有任何的报错,感觉不太好排查
    OysterQAQ
        3
    OysterQAQ  
       2020-10-22 13:15:16 +08:00
    @echooo0 可能有别的长时间执行的任务占用了线程池中所有的空闲线程 不过也有可能是别的 cron 任务占用了 cron 的线程 jstack 一下呗
    OysterQAQ
        4
    OysterQAQ  
       2020-10-22 13:16:16 +08:00
    报错是不可能报错的 如果队列大小和拒绝策略有设置的话,也没那么快报错
    echooo0
        5
    echooo0  
    OP
       2020-10-22 13:17:10 +08:00
    @OysterQAQ 发现了一个问题,线程池的 id 非常大,755 这种

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

    感觉可能和这个有关系,但是开了这么多线程,也没 out of memory 错误报出来,只是 hang 住了,也是很奇怪。。。
    OysterQAQ
        6
    OysterQAQ  
       2020-10-22 13:25:18 +08:00
    @echooo0 哈,,我一般都是对这两个注解指定自己的线程池的 根据业务来新建线程池再在注解中指定使用哪个线程池 不至于互相干扰 我觉得没报错大多是因为根本就在队列中没执行 查查同样用了 cron 的有没有什么耗时很长的大任务
    echooo0
        7
    echooo0  
    OP
       2020-10-22 14:41:23 +08:00
    @OysterQAQ 你这种 "根据业务来新建线程池再在注解中指定使用哪个线程池" , 可能控制的更精细化一些
    我目前修改的办法,是实现了 AsyncConfigurer,定义了全局的线程池都用 ThreadPoolTaskExecutor 来做了
    Vkery
        8
    Vkery  
       2020-10-22 15:01:00 +08:00
    接个 spring boot admin 看看线程状态
    我以前遇到过也是定时任务过段时间就停了 后来发现好像是线程池用完了,因为有个定时任务,调用第三方的接口,因为没有超时机制,一直等待在那
    PIECExx
        9
    PIECExx  
       2020-10-22 16:09:13 +08:00
    @Scheduled 也可以配置多个并发线程数,也可以达到你说的这个效果。
    如果配合 @Async 用,那你不得说下这个池子的配置么?
    echooo0
        10
    echooo0  
    OP
       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
        11
    echooo0  
    OP
       2020-10-23 00:51:45 +08:00
    搞定了,实现 ThreadPoolTaskScheduler 即可
    xkzhangsan
        12
    xkzhangsan  
       2020-10-23 08:18:08 +08:00 via Android
    自定义一个线程池比较好
    echooo0
        13
    echooo0  
    OP
       2020-10-23 11:31:33 +08:00
    @PIECExx 试了下,好像如果直接去配置 @Scheduled 的并发,都没法设置 maximumPoolSize 这个参数,有点迷
    不知道官方为什么有这么个逻辑
    PIECExx
        14
    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
        15
    echooo0  
    OP
       2020-10-24 00:47:09 +08:00
    @PIECExx 对,源码那个我看了,一般规范的来说,maximumPoolSize 设置为 Integer.MAX_VALUE 不是不太好嘛

    Executors 下面那些,好像只有 newScheduledThreadPool 适合的,taskRegistrar.setScheduler 对参数类型有限制
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   5695 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 24ms · UTC 03:08 · PVG 11:08 · LAX 19:08 · JFK 22:08
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.