Spring Scheduler因为是单线程执行,所以容易出现部分任务耗时太长,虽然可以使用多线程的方式来提升效率,但是也担心定时任务占用太多的资源,所以还是尽可能地优化定时任务的耗时,那我们首先就可以对耗时进行监控
Spring Scheduler使用ScheduledAnnotationBeanPostProcessor#createRunnable
来创建定时任务的执行Runnable(实际类型是ScheduledMethodRunnable),那么我们就可以对该ScheduledMethodRunnable进行扩展,增加任务耗时,设置traceId,方便进行日志分析等
扩展ScheduledMethodRunnable
- 设置traceId
- 监控耗时
@Slf4j
public class TenmaoScheduledMethodRunnable extends ScheduledMethodRunnable {
public TenmaoScheduledMethodRunnable(Object target, Method method) {
super(target, method);
}
public TenmaoScheduledMethodRunnable(Object target, String methodName) throws NoSuchMethodException {
super(target, methodName);
}
@Override
public void run() {
//设置traceId,方便进行日志跟踪
MDC.put("traceId", UUID.randomUUID().toString().replaceAll("-", ""));
log.info("{}: start to run", getMethod());
long startMillis = System.currentTimeMillis();
try {
super.run();
} finally {
//监控耗时
long endMillis = System.currentTimeMillis();
log.info("{}: cost {} millis", getMethod(), endMillis - startMillis);
MDC.remove("traceId");
}
}
}
扩展ScheduledAnnotationBeanPostProcessor
让ScheduledAnnotationBeanPostProcessor使用TenmaoScheduledMethodRunnable作为定时任务真正的执行逻辑
@Component
public class TenmaoScheduledAnnotationBeanPostProcessor extends ScheduledAnnotationBeanPostProcessor {
@Override
protected Runnable createRunnable(Object target, Method method) {
//这两行代码从ScheduledAnnotationBeanPostProcessor#createRunnable复制过来
Assert.isTrue(method.getParameterCount() == 0, "Only no-arg methods may be annotated with @Scheduled");
Method invocableMethod = AopUtils.selectInvocableMethod(method, target.getClass());
//把ScheduledMethodRunnable替换为TenmaoScheduledMethodRunnable
return new TenmaoScheduledMethodRunnable(target, invocableMethod);
}
}
注意
- 如果没有在
TenmaoScheduledAnnotationBeanPostProcessor
增加@Component注解,那么@EnableScheduling
一定要去掉
思考
- 是不是也可以使用Aop的方式实现这一点呢? 貌似有一点麻烦,因为很多定时任务是private,并不会被代理
网友评论