From af463c656c28e355f062d3abc30435cffa21f76a Mon Sep 17 00:00:00 2001 From: jaysunxiao Date: Sat, 18 Sep 2021 10:51:45 +0800 Subject: [PATCH] =?UTF-8?q?perf[scheduler]:=20=E8=AE=A9cron=E8=A1=A8?= =?UTF-8?q?=E8=BE=BE=E5=BC=8F=E5=9C=A8=E5=85=B6=E5=AE=83=E6=A8=A1=E5=9D=97?= =?UTF-8?q?=E5=90=AF=E5=8A=A8=E5=AE=8C=E6=88=90=E8=BF=87=E5=90=8E=E5=86=8D?= =?UTF-8?q?=E6=89=A7=E8=A1=8C=EF=BC=8C=E5=87=8F=E5=B0=91=E9=87=8D=E5=A4=8D?= =?UTF-8?q?=E7=9A=84=E9=81=8D=E5=8E=86=E6=8F=90=E5=8D=87=E4=BA=86=E4=B8=80?= =?UTF-8?q?=E7=82=B9=E6=80=A7=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../zfoo/boot/SchedulerAutoConfiguration.java | 7 -- .../com/zfoo/scheduler/SchedulerContext.java | 55 ++++++++++++ .../zfoo/scheduler/manager/SchedulerBus.java | 44 +++++----- .../schema/SchedulerDefinitionParser.java | 6 -- .../schema/SchedulerRegisterProcessor.java | 83 ------------------- .../com/zfoo/scheduler/util/TimeUtils.java | 10 +-- 6 files changed, 81 insertions(+), 124 deletions(-) delete mode 100644 scheduler/src/main/java/com/zfoo/scheduler/schema/SchedulerRegisterProcessor.java diff --git a/boot/src/main/java/com/zfoo/boot/SchedulerAutoConfiguration.java b/boot/src/main/java/com/zfoo/boot/SchedulerAutoConfiguration.java index ae2117c4..ac9c1af2 100644 --- a/boot/src/main/java/com/zfoo/boot/SchedulerAutoConfiguration.java +++ b/boot/src/main/java/com/zfoo/boot/SchedulerAutoConfiguration.java @@ -13,7 +13,6 @@ package com.zfoo.boot; import com.zfoo.scheduler.SchedulerContext; -import com.zfoo.scheduler.schema.SchedulerRegisterProcessor; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -25,12 +24,6 @@ import org.springframework.context.annotation.Configuration; @Configuration(proxyBeanMethods = false) public class SchedulerAutoConfiguration { - @Bean - @ConditionalOnMissingBean - public SchedulerRegisterProcessor schedulerRegisterProcessor() { - return new SchedulerRegisterProcessor(); - } - @Bean @ConditionalOnMissingBean public SchedulerContext schedulerContext() { diff --git a/scheduler/src/main/java/com/zfoo/scheduler/SchedulerContext.java b/scheduler/src/main/java/com/zfoo/scheduler/SchedulerContext.java index 18b8cbad..f180f1d6 100644 --- a/scheduler/src/main/java/com/zfoo/scheduler/SchedulerContext.java +++ b/scheduler/src/main/java/com/zfoo/scheduler/SchedulerContext.java @@ -13,8 +13,12 @@ package com.zfoo.scheduler; +import com.zfoo.protocol.collection.ArrayUtils; import com.zfoo.protocol.util.ReflectionUtils; +import com.zfoo.protocol.util.StringUtils; import com.zfoo.scheduler.manager.SchedulerBus; +import com.zfoo.scheduler.model.anno.Scheduler; +import com.zfoo.scheduler.model.vo.SchedulerDefinition; import com.zfoo.util.ThreadUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -26,6 +30,7 @@ import org.springframework.context.event.ContextRefreshedEvent; import org.springframework.core.Ordered; import java.lang.reflect.Field; +import java.lang.reflect.Modifier; import java.util.concurrent.ScheduledExecutorService; /** @@ -83,11 +88,61 @@ public class SchedulerContext implements ApplicationListener= 1) { + throw new IllegalArgumentException(StringUtils.format("[class:{}] [method:{}] can not have any parameters", bean.getClass(), method.getName())); + } + + var methodName = method.getName(); + + if (!Modifier.isPublic(method.getModifiers())) { + throw new IllegalArgumentException(StringUtils.format("[class:{}] [method:{}] must use 'public' as modifier!", bean.getClass().getName(), methodName)); + } + + if (Modifier.isStatic(method.getModifiers())) { + throw new IllegalArgumentException(StringUtils.format("[class:{}] [method:{}] can not use 'static' as modifier!", bean.getClass().getName(), methodName)); + } + + if (!methodName.startsWith("cron")) { + throw new IllegalArgumentException(StringUtils.format("[class:{}] [method:{}] must start with 'cron' as method name!" + , bean.getClass().getName(), methodName)); + } + + var scheduler = SchedulerDefinition.valueOf(schedulerMethod.cron(), bean, method); + SchedulerBus.registerScheduler(scheduler); + } + } catch (Throwable t) { + throw new RuntimeException(t); + } + } + } + @Override public int getOrder() { return Ordered.LOWEST_PRECEDENCE; diff --git a/scheduler/src/main/java/com/zfoo/scheduler/manager/SchedulerBus.java b/scheduler/src/main/java/com/zfoo/scheduler/manager/SchedulerBus.java index b3c6e503..88048f3d 100644 --- a/scheduler/src/main/java/com/zfoo/scheduler/manager/SchedulerBus.java +++ b/scheduler/src/main/java/com/zfoo/scheduler/manager/SchedulerBus.java @@ -14,14 +14,12 @@ package com.zfoo.scheduler.manager; import com.zfoo.protocol.collection.CollectionUtils; -import com.zfoo.protocol.util.JsonUtils; import com.zfoo.scheduler.SchedulerContext; import com.zfoo.scheduler.model.vo.SchedulerDefinition; import com.zfoo.scheduler.util.TimeUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.util.Comparator; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.Executors; @@ -41,13 +39,13 @@ public abstract class SchedulerBus { /** * 上一次trigger触发时间 */ - private static long lastTriggerTimestamp = 0; + private static long lastTriggerTimestamp = 0L; /** * 在scheduler中,最小的triggerTimestamp */ - private static long minSchedulerTriggerTimestamp = 0; + private static long minTriggerTimestamp = 0L; public static final long TRIGGER_MILLIS_INTERVAL = TimeUtils.MILLIS_PER_SECOND; @@ -65,18 +63,18 @@ public abstract class SchedulerBus { } catch (Exception e) { logger.error("scheduler triggers an error.", e); } - }, 7 * TimeUtils.MILLIS_PER_SECOND, TRIGGER_MILLIS_INTERVAL, TimeUnit.MILLISECONDS); + }, 0, TRIGGER_MILLIS_INTERVAL, TimeUnit.MILLISECONDS); } - private static long minSchedulerTriggerTimestamp() { - var minSchedulerOptional = schedulerDefList.stream().min(Comparator.comparingLong(schedulerDef -> schedulerDef.getTriggerTimestamp())); - if (minSchedulerOptional.isPresent()) { - return minSchedulerOptional.get().getTriggerTimestamp(); - } else { - logger.error("schedulerDefList:[{}] has no minSchedulerTriggerTimestamp to return. ", JsonUtils.object2String(schedulerDefList)); - return 0; + public static void refreshMinTriggerTimestamp() { + var minTimestamp = Long.MAX_VALUE; + for (var scheduler : schedulerDefList) { + if (scheduler.getTriggerTimestamp() < minTimestamp) { + minTimestamp = scheduler.getTriggerTimestamp(); + } } + minTriggerTimestamp = minTimestamp; } /** @@ -97,32 +95,36 @@ public abstract class SchedulerBus { var nextTriggerTimestamp = TimeUtils.getNextTimestampByCronExpression(schedulerDef.getCronExpression(), timestamp); schedulerDef.setTriggerTimestamp(nextTriggerTimestamp); } - minSchedulerTriggerTimestamp = minSchedulerTriggerTimestamp(); + refreshMinTriggerTimestamp(); } // diff > 0, 没有人调整时间或者有人向后调整过机器时间,可以忽略,因为向后调整时间时间戳一定会大于triggerTimestamp,所以一定会触发 lastTriggerTimestamp = timestamp; // 如果minSchedulerTriggerTimestamp大于timestamp,说明没有可执行的scheduler - if (timestamp < minSchedulerTriggerTimestamp) { + if (timestamp < minTriggerTimestamp) { return; } - for (var schedulerDef : schedulerDefList) { - if (timestamp >= schedulerDef.getTriggerTimestamp()) { + var minTimestamp = Long.MAX_VALUE; + for (var scheduler : schedulerDefList) { + if (timestamp >= scheduler.getTriggerTimestamp()) { // 到达触发时间,则执行runnable方法 - schedulerDef.getScheduler().invoke(); + scheduler.getScheduler().invoke(); // 重新设置下一次的触发时间戳 - var nextTriggerTimestamp = TimeUtils.getNextTimestampByCronExpression(schedulerDef.getCronExpression(), timestamp); - schedulerDef.setTriggerTimestamp(nextTriggerTimestamp); + var nextTriggerTimestamp = TimeUtils.getNextTimestampByCronExpression(scheduler.getCronExpression(), timestamp); + scheduler.setTriggerTimestamp(nextTriggerTimestamp); + } + if (scheduler.getTriggerTimestamp() < minTimestamp) { + minTimestamp = scheduler.getTriggerTimestamp(); } } - minSchedulerTriggerTimestamp = minSchedulerTriggerTimestamp(); + minTriggerTimestamp = minTimestamp; } public static void registerScheduler(SchedulerDefinition scheduler) { schedulerDefList.add(scheduler); - minSchedulerTriggerTimestamp = minSchedulerTriggerTimestamp(); + refreshMinTriggerTimestamp(); } diff --git a/scheduler/src/main/java/com/zfoo/scheduler/schema/SchedulerDefinitionParser.java b/scheduler/src/main/java/com/zfoo/scheduler/schema/SchedulerDefinitionParser.java index 52749559..05e2c62c 100644 --- a/scheduler/src/main/java/com/zfoo/scheduler/schema/SchedulerDefinitionParser.java +++ b/scheduler/src/main/java/com/zfoo/scheduler/schema/SchedulerDefinitionParser.java @@ -41,12 +41,6 @@ public class SchedulerDefinitionParser implements BeanDefinitionParser { builder = BeanDefinitionBuilder.rootBeanDefinition(clazz); parserContext.getRegistry().registerBeanDefinition(name, builder.getBeanDefinition()); - // 注册SchedulerRegisterProcessor - clazz = SchedulerRegisterProcessor.class; - name = StringUtils.uncapitalize(clazz.getName()); - builder = BeanDefinitionBuilder.rootBeanDefinition(clazz); - parserContext.getRegistry().registerBeanDefinition(name, builder.getBeanDefinition()); - return builder.getBeanDefinition(); } diff --git a/scheduler/src/main/java/com/zfoo/scheduler/schema/SchedulerRegisterProcessor.java b/scheduler/src/main/java/com/zfoo/scheduler/schema/SchedulerRegisterProcessor.java deleted file mode 100644 index d8c91776..00000000 --- a/scheduler/src/main/java/com/zfoo/scheduler/schema/SchedulerRegisterProcessor.java +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright (C) 2020 The zfoo Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and limitations under the License. - */ - -package com.zfoo.scheduler.schema; - -import com.zfoo.protocol.collection.ArrayUtils; -import com.zfoo.protocol.util.ReflectionUtils; -import com.zfoo.protocol.util.StringUtils; -import com.zfoo.scheduler.manager.SchedulerBus; -import com.zfoo.scheduler.model.anno.Scheduler; -import com.zfoo.scheduler.model.vo.SchedulerDefinition; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.BeansException; -import org.springframework.beans.factory.config.BeanPostProcessor; - -import java.lang.reflect.Modifier; - -/** - * @author jaysunxiao - * @version 3.0 - */ -public class SchedulerRegisterProcessor implements BeanPostProcessor { - - private static final Logger logger = LoggerFactory.getLogger(SchedulerRegisterProcessor.class); - - @Override - public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { - var clazz = bean.getClass(); - var methods = ReflectionUtils.getMethodsByAnnoInPOJOClass(bean.getClass(), Scheduler.class); - - if (ArrayUtils.isEmpty(methods)) { - return bean; - } - - if (!ReflectionUtils.isPojoClass(clazz)) { - logger.warn("调度注册类[{}]不是POJO类,父类的调度不会被扫描到", clazz); - } - - try { - for (var method : methods) { - var schedulerMethod = method.getAnnotation(Scheduler.class); - - var paramClazzs = method.getParameterTypes(); - if (paramClazzs.length >= 1) { - throw new IllegalArgumentException(StringUtils.format("[class:{}] [method:{}] can not have any parameters", bean.getClass(), method.getName())); - } - - var methodName = method.getName(); - - if (!Modifier.isPublic(method.getModifiers())) { - throw new IllegalArgumentException(StringUtils.format("[class:{}] [method:{}] must use 'public' as modifier!", bean.getClass().getName(), methodName)); - } - - if (Modifier.isStatic(method.getModifiers())) { - throw new IllegalArgumentException(StringUtils.format("[class:{}] [method:{}] can not use 'static' as modifier!", bean.getClass().getName(), methodName)); - } - - if (!methodName.startsWith("cron")) { - throw new IllegalArgumentException(StringUtils.format("[class:{}] [method:{}] must start with 'cron' as method name!" - , bean.getClass().getName(), methodName)); - } - - var scheduler = SchedulerDefinition.valueOf(schedulerMethod.cron(), bean, method); - SchedulerBus.registerScheduler(scheduler); - } - } catch (Throwable t) { - throw new RuntimeException(t); - } - return bean; - } - -} diff --git a/scheduler/src/main/java/com/zfoo/scheduler/util/TimeUtils.java b/scheduler/src/main/java/com/zfoo/scheduler/util/TimeUtils.java index 8a5326f8..ce45e270 100644 --- a/scheduler/src/main/java/com/zfoo/scheduler/util/TimeUtils.java +++ b/scheduler/src/main/java/com/zfoo/scheduler/util/TimeUtils.java @@ -24,7 +24,6 @@ import java.time.temporal.TemporalAdjusters; import java.util.Calendar; import java.util.Date; import java.util.TimeZone; -import java.util.concurrent.TimeUnit; /** * @author jaysunxiao @@ -81,12 +80,9 @@ public abstract class TimeUtils { }; static { - SchedulerBus.schedule(new Runnable() { - @Override - public void run() { - currentTimeMillis(); - } - }, 0, TimeUnit.SECONDS); + currentTimeMillis(); + // 调用一下静态方法,使SchedulerBus静态代码块初始化 + SchedulerBus.refreshMinTriggerTimestamp(); } /**