性能文章>一次“诡异”的JVM缓存加载问题排查>

一次“诡异”的JVM缓存加载问题排查原创

2年前
7543518

项目中使用@Scheduled注解(Spring注解)来定时(每隔五分钟)刷新JVM缓存。

但测试环境和线上环境出现不一样的效果(如下图),测试环境每隔5分钟刷新一次,而线上环境刷新的时间间隔远远超过5分钟。测试环境的时间间隔均正常,而线上环境的时间间隔均不正常。

测试环境

image.png

线上环境

image.png

项目中有若干加载缓存的类,加载缓存的代码如下。

  • @Component
  • public class XXXCache {
  • // 5分钟
  • private final long PERIOD = 5L * 60L * 1000L;
  •  
  • @Scheduled(fixedRate = PERIOD, initialDelay = PERIOD)
  • public void loadCache() {
  • //TODO
  • }
  • }
  •  

分析:测试环境与线上环境代码一致,但测试环境加载的缓存数据量偏少。

猜测:由于数据量不同,导致测试环境和线上环境的执行时间间隔出现差异的现象。

进一步猜测:某些类加载缓存的时间偏长,导致其他类加载缓存的动作长时间处于等待状态。

那么问题来了,被@Scheduled注解的方法是并发执行的呢?还是串行执行的呢?

通过断点调试,发现在org.springframework.scheduling.config.ScheduledTaskRegistrar#scheduleTasks中,创建了单个线程的线程池,而这个线程池是用来执行整个项目中所有被@Scheduled注解的方法。当某个方法执行时间过久,那么其他方法的任务只能等待,从而导致其他缓存的加载时间间隔被拉长。

image.png

解决问题的思路:

  1. 找出加载数据量较多的方法,提高其执行间隔,降低单位时间内的加载次数。

  2. 修改上述线程池的大小,提高并发。

思路1比较好处理,如何处理思路2呢?


通过分析代码org.springframework.scheduling.annotation.ScheduledAnnotationBeanPostProcessor#finishRegistration,发现可以使用org.springframework.scheduling.annotation.SchedulingConfigurer来定制org.springframework.scheduling.TaskScheduler

见上文截图中创建单线程线程池代码所在的if语句,只要我们提前创建TaskScheduler,那么代码就不会再去创建单线程线程池的TaskScheduler。在我们自己创建TaskScheduler时,提高线程池的大小,从而达到并发的效果。

上代码

  • @Configuration
  • public class ScheduleConfig implements SchedulingConfigurer {
  •  
  • @Override
  • public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
  • //初始化线程池
  • ScheduledThreadPoolExecutor localExecutor = new ScheduledThreadPoolExecutor(3);
  •  
  • ConcurrentTaskScheduler taskScheduler = new ConcurrentTaskScheduler(localExecutor);
  • taskRegistrar.setScheduler(taskScheduler);
  • }
  • }
  •  
点赞收藏
分类:标签:
猪杂汤饭
请先登录,查看5条精彩评论吧
快去登录吧,你将获得
  • 浏览更多精彩评论
  • 和开发者讨论交流,共同进步

为你推荐

【全网首发】一次想不到的 Bootstrap 类加载器带来的 Native 内存泄露分析

【全网首发】一次想不到的 Bootstrap 类加载器带来的 Native 内存泄露分析

记一次“雪花算法”造成的生产事故的排查记录

记一次“雪花算法”造成的生产事故的排查记录

【全网首发】一次疑似 JVM Native 内存泄露的问题分析

【全网首发】一次疑似 JVM Native 内存泄露的问题分析

解读JVM级别本地缓存Caffeine青出于蓝的要诀2 —— 弄清楚Caffeine的同步、异步回源方式

解读JVM级别本地缓存Caffeine青出于蓝的要诀2 —— 弄清楚Caffeine的同步、异步回源方式

单服务并发出票实践

单服务并发出票实践

刺激,线程池的一个BUG直接把CPU干到100%了。

刺激,线程池的一个BUG直接把CPU干到100%了。

18
5