mirror of
https://github.com/tiennm99/zfoo.git
synced 2026-10-15 10:15:59 +00:00
init project
This commit is contained in:
1 parent
b7f28da485
commit
bce060a28e
573 files changed
+84513
No files matched your search
@@ -0,0 +1,35 @@
|
||||
### Ⅰ. 注意事项
|
||||
|
||||
- 每秒钟执行一次SchedulerManager.triggerPerSecond()方法,循环遍历可执行的scheduler
|
||||
- triggerPerSecond在计算下一次triggerTimestamp会很耗时间,所以尽量避免配置一秒或者间隔时间很短的cron表达式
|
||||
- SchedulerManager的executor只有一条线程,所以使用者要避免做耗时和阻塞的运算,如果有这样的需求可以抛到其它线程池
|
||||
- 时间可以向前调,也可以向后调,都会出发trigger
|
||||
|
||||
### Ⅱ. Cron Expression Example
|
||||
|
||||
```
|
||||
30 * * * * ? 每半分钟触发任务
|
||||
30 10 * * * ? 每小时的10分30秒触发任务
|
||||
30 10 1 * * ? 每天1点10分30秒触发任务
|
||||
30 10 1 20 * ? 每月20号1点10分30秒触发任务
|
||||
30 10 1 20 10 ? * 每年10月20号1点10分30秒触发任务
|
||||
30 10 1 20 10 ? 2018 2018年10月20号1点10分30秒触发任务
|
||||
30 10 1 ? 10 * 2018 2018年10月每天20号1点10分30秒触发任务
|
||||
30 10 1 ? 10 SUN 2018 2018年10月每周日1点10分30秒触发任务
|
||||
15,30,45 * * * * ? 每分钟的15,30,45秒个触发一次
|
||||
15-45 * * * * ? 每分钟的15秒到45秒内,每秒都触发一次
|
||||
15/5 * * * * ? 每分钟的15秒开始触发,每隔5秒触发一次
|
||||
15-30/5 * * * * ? 每分钟的15秒到30秒之间开始触发,每隔5秒触发一次
|
||||
0 0/3 * * * ? 每小时的第0分0秒开始,没三分钟触发一次
|
||||
0 15 10 ? * MON-FRI 星期一到星期五每天10点15分0秒触发一次
|
||||
0 15 10 L * ? 每个月的最后一天的10点15分0秒触发任务
|
||||
0 15 10 LW * ? 每个月最后一个工作日的10点15分0秒触发任务
|
||||
0 15 10 ? * 5L 每个月最后一个星期四的10点15分0秒触发任务
|
||||
0 15 10 ? * 5#3 每个月第三周的星期四的10点15分0秒触发任务
|
||||
|
||||
说明:
|
||||
*(星号):代表任何时刻都接受癿意思
|
||||
,(逗号):代表分隔时段的意思
|
||||
-(减号):代表一段时间范围内
|
||||
/n(斜线):每隔n单位间隔
|
||||
```
|
||||
@@ -0,0 +1,243 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<groupId>com.zfoo</groupId>
|
||||
<artifactId>scheduler</artifactId>
|
||||
<version>3.0</version>
|
||||
|
||||
<packaging>jar</packaging>
|
||||
|
||||
|
||||
<properties>
|
||||
<!-- 本项目的其它module版本号 -->
|
||||
<zfoo.event.version>3.0</zfoo.event.version>
|
||||
<zfoo.hotswap.version>3.0</zfoo.hotswap.version>
|
||||
<zfoo.monitor.version>3.0</zfoo.monitor.version>
|
||||
<zfoo.net.version>3.0</zfoo.net.version>
|
||||
<zfoo.scheduler.version>3.0</zfoo.scheduler.version>
|
||||
<zfoo.storage.version>3.0</zfoo.storage.version>
|
||||
<zfoo.orm.version>3.0</zfoo.orm.version>
|
||||
<zfoo.protocol.version>3.0</zfoo.protocol.version>
|
||||
<zfoo.util.version>3.0</zfoo.util.version>
|
||||
|
||||
|
||||
<!-- 核心spring框架 -->
|
||||
<spring.version>5.3.4</spring.version>
|
||||
<spring.boot.version>2.4.3</spring.boot.version>
|
||||
|
||||
|
||||
<!-- 工具包 -->
|
||||
<commons-codec.version>1.15</commons-codec.version>
|
||||
<commons-io.version>2.8.0</commons-io.version>
|
||||
<commons-collections.version>4.4</commons-collections.version>
|
||||
<commons-lang.version>3.12.0</commons-lang.version>
|
||||
<commons-fileupload.version>1.4</commons-fileupload.version>
|
||||
<commons-logging.version>1.2</commons-logging.version>
|
||||
<commons-log4j.version>2.14.0</commons-log4j.version>
|
||||
<httpcomponents.version>4.5.13</httpcomponents.version>
|
||||
<httpcore.version>4.4.14</httpcore.version>
|
||||
<google.guava.version>30.1-jre</google.guava.version>
|
||||
<google.protobuf.version>3.9.1</google.protobuf.version>
|
||||
<google.gson.version>2.8.6</google.gson.version>
|
||||
<kryo.version>5.0.3</kryo.version>
|
||||
<caffeine.version>2.8.8</caffeine.version>
|
||||
<jctools.version>3.2.0</jctools.version>
|
||||
<hutool.version>5.5.9</hutool.version>
|
||||
<oshi.version>5.7.0</oshi.version>
|
||||
<snakeyaml.version>1.28</snakeyaml.version>
|
||||
|
||||
|
||||
<!-- json和xml解析包 -->
|
||||
<jackson.version>2.12.1</jackson.version>
|
||||
<fastjson.version>1.2.51</fastjson.version>
|
||||
<!-- office文档解析包 -->
|
||||
<poi.version>4.1.2</poi.version>
|
||||
<!-- 字节码增强 -->
|
||||
<javassist.version>3.27.0-GA</javassist.version>
|
||||
<bytebuddy.version>1.10.22</bytebuddy.version>
|
||||
|
||||
<!-- 网络通讯框架 -->
|
||||
<netty.version>4.1.63.Final</netty.version>
|
||||
|
||||
<!-- 分布式zookeeper核心依赖包 -->
|
||||
<zookeeper.version>3.6.1</zookeeper.version>
|
||||
<curator.version>5.1.0</curator.version>
|
||||
|
||||
<!-- 数据库和缓存 -->
|
||||
<mongodb-driver-sync.version>4.2.1</mongodb-driver-sync.version>
|
||||
<jedis.version>3.3.0</jedis.version>
|
||||
|
||||
<!-- 消息队列中间件 -->
|
||||
<rocketmq.version>4.5.2</rocketmq.version>
|
||||
|
||||
<!-- elastic search 中间件 -->
|
||||
<elastic.search.version>7.9.3</elastic.search.version>
|
||||
<elastic.search.spring.version>4.1.5</elastic.search.spring.version>
|
||||
<lucene.version>8.6.2</lucene.version>
|
||||
|
||||
|
||||
<slf4j.version>1.7.30</slf4j.version>
|
||||
<logback.version>1.2.3</logback.version>
|
||||
|
||||
<junit.version>4.13.1</junit.version>
|
||||
|
||||
<!-- java版本和文件编码 -->
|
||||
<java.version>11</java.version>
|
||||
<file.encoding>UTF-8</file.encoding>
|
||||
<jakarta.version>1.3.5</jakarta.version>
|
||||
|
||||
<!-- maven核心插件 -->
|
||||
<maven-clean-plugin.version>3.1.0</maven-clean-plugin.version>
|
||||
<maven-compiler-plugin.version>3.8.1</maven-compiler-plugin.version>
|
||||
<maven-resources-plugin.version>3.2.0</maven-resources-plugin.version>
|
||||
<maven-surefire-plugin.version>3.0.0-M5</maven-surefire-plugin.version>
|
||||
<maven-jar-plugin.version>3.2.0</maven-jar-plugin.version>
|
||||
<maven-shade-plugin.version>3.2.4</maven-shade-plugin.version>
|
||||
<versions-maven-plugin.version>2.8.1</versions-maven-plugin.version>
|
||||
|
||||
|
||||
<project.build.sourceEncoding>${file.encoding}</project.build.sourceEncoding>
|
||||
<maven.compiler.encoding>${file.encoding}</maven.compiler.encoding>
|
||||
</properties>
|
||||
|
||||
|
||||
<dependencies>
|
||||
<!-- 依赖的util类库 -->
|
||||
<dependency>
|
||||
<groupId>com.zfoo</groupId>
|
||||
<artifactId>util</artifactId>
|
||||
<version>${zfoo.util.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.netty</groupId>
|
||||
<artifactId>netty-all</artifactId>
|
||||
<version>${netty.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 动态生成二进制字节码的javassist类库 -->
|
||||
<dependency>
|
||||
<groupId>org.javassist</groupId>
|
||||
<artifactId>javassist</artifactId>
|
||||
<version>${javassist.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 依赖的Spring模块类库 -->
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-context</artifactId>
|
||||
<version>${spring.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- slf4j-api -->
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-api</artifactId>
|
||||
<version>${slf4j.version}</version>
|
||||
</dependency>
|
||||
<!-- logback核心包 -->
|
||||
<dependency>
|
||||
<groupId>ch.qos.logback</groupId>
|
||||
<artifactId>logback-core</artifactId>
|
||||
<version>${logback.version}</version>
|
||||
</dependency>
|
||||
<!-- logback的sl4j的实现 -->
|
||||
<dependency>
|
||||
<groupId>ch.qos.logback</groupId>
|
||||
<artifactId>logback-classic</artifactId>
|
||||
<version>${logback.version}</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<artifactId>slf4j-api</artifactId>
|
||||
<groupId>org.slf4j</groupId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<!-- 依赖的测试库 -->
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<version>${junit.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<sourceDirectory>src/main/java</sourceDirectory>
|
||||
<testSourceDirectory>src/test/java</testSourceDirectory>
|
||||
|
||||
<plugins>
|
||||
|
||||
<!-- 清理插件 -->
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-clean-plugin</artifactId>
|
||||
<version>${maven-clean-plugin.version}</version>
|
||||
</plugin>
|
||||
|
||||
<!-- 编译插件 -->
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>${maven-compiler-plugin.version}</version>
|
||||
<configuration>
|
||||
<source>${java.version}</source>
|
||||
<target>${java.version}</target>
|
||||
<encoding>${file.encoding}</encoding>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
|
||||
<!-- resource资源管理插件 -->
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-resources-plugin</artifactId>
|
||||
<version>${maven-resources-plugin.version}</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>copy-resources</id>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>copy-resources</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<encoding>${file.encoding}</encoding>
|
||||
<outputDirectory>${project.build.directory}/resource</outputDirectory>
|
||||
<resources>
|
||||
<resource>
|
||||
<directory>src/main/resources/</directory>
|
||||
<filtering>false</filtering>
|
||||
</resource>
|
||||
</resources>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
|
||||
|
||||
<!-- 测试插件 -->
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<version>${maven-surefire-plugin.version}</version>
|
||||
<configuration>
|
||||
<forkMode>once</forkMode>
|
||||
<threadCount>10</threadCount>
|
||||
<argLine>-Dfile.encoding=${file.encoding}</argLine>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-jar-plugin</artifactId>
|
||||
<version>${maven-jar-plugin.version}</version>
|
||||
</plugin>
|
||||
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,119 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import com.zfoo.protocol.util.ReflectionUtils;
|
||||
import com.zfoo.scheduler.manager.ISchedulerManager;
|
||||
import com.zfoo.scheduler.manager.SchedulerManager;
|
||||
import com.zfoo.scheduler.schema.SchedulerRegisterProcessor;
|
||||
import com.zfoo.util.ThreadUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationListener;
|
||||
import org.springframework.context.event.ApplicationContextEvent;
|
||||
import org.springframework.context.event.ContextClosedEvent;
|
||||
import org.springframework.context.event.ContextRefreshedEvent;
|
||||
import org.springframework.core.Ordered;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
|
||||
/**
|
||||
* @author jaysunxiao
|
||||
* @version 3.0
|
||||
*/
|
||||
public class SchedulerContext implements ApplicationListener<ApplicationContextEvent>, Ordered {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(SchedulerContext.class);
|
||||
|
||||
private static SchedulerContext instance;
|
||||
|
||||
private static boolean stop = false;
|
||||
|
||||
private ApplicationContext applicationContext;
|
||||
|
||||
|
||||
public static SchedulerContext getSchedulerContext() {
|
||||
return instance;
|
||||
}
|
||||
|
||||
public static ApplicationContext getApplicationContext() {
|
||||
return instance.applicationContext;
|
||||
}
|
||||
|
||||
|
||||
public static ISchedulerManager getSchedulerManager() {
|
||||
return SchedulerManager.getInstance();
|
||||
}
|
||||
|
||||
public static boolean isStop() {
|
||||
return stop;
|
||||
}
|
||||
|
||||
|
||||
public synchronized static void shutdown() {
|
||||
if (stop) {
|
||||
return;
|
||||
}
|
||||
|
||||
stop = true;
|
||||
|
||||
ISchedulerManager schedulerManager = getSchedulerManager();
|
||||
if (schedulerManager == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
Field field = SchedulerManager.class.getDeclaredField("executor");
|
||||
ReflectionUtils.makeAccessible(field);
|
||||
var executor = (ScheduledExecutorService) ReflectionUtils.getField(field, schedulerManager);
|
||||
ThreadUtils.shutdown(executor);
|
||||
} catch (Throwable e) {
|
||||
logger.error("Scheduler thread pool failed shutdown.", e);
|
||||
return;
|
||||
}
|
||||
|
||||
logger.info("Scheduler shutdown gracefully.");
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onApplicationEvent(ApplicationContextEvent event) {
|
||||
if (event instanceof ContextRefreshedEvent) {
|
||||
if (instance != null) {
|
||||
return;
|
||||
}
|
||||
// 初始化上下文
|
||||
SchedulerContext.instance = this;
|
||||
instance.applicationContext = event.getApplicationContext();
|
||||
|
||||
var beanNames = applicationContext.getBeanDefinitionNames();
|
||||
var processor = applicationContext.getBean(SchedulerRegisterProcessor.class);
|
||||
|
||||
for (var beanName : beanNames) {
|
||||
processor.postProcessAfterInitialization(applicationContext.getBean(beanName), beanName);
|
||||
}
|
||||
|
||||
} else if (event instanceof ContextClosedEvent) {
|
||||
shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOrder() {
|
||||
return Ordered.LOWEST_PRECEDENCE;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* 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.manager;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* @author jaysunxiao
|
||||
* @version 3.0
|
||||
*/
|
||||
public interface ISchedulerManager {
|
||||
|
||||
/**
|
||||
* 不断执行的周期循环任务
|
||||
*/
|
||||
void scheduleAtFixedRate(Runnable runnable, long period, TimeUnit unit);
|
||||
|
||||
/**
|
||||
* 固定延迟执行的任务
|
||||
*/
|
||||
void schedule(Runnable runnable, long delay, TimeUnit unit);
|
||||
|
||||
|
||||
/**
|
||||
* cron表达式执行的任务
|
||||
*/
|
||||
void scheduleCron(Runnable runnable, String cron);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,240 @@
|
||||
/*
|
||||
* 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.manager;
|
||||
|
||||
import com.zfoo.protocol.collection.CollectionUtils;
|
||||
import com.zfoo.protocol.util.JsonUtils;
|
||||
import com.zfoo.protocol.util.ReflectionUtils;
|
||||
import com.zfoo.protocol.util.StringUtils;
|
||||
import com.zfoo.scheduler.SchedulerContext;
|
||||
import com.zfoo.scheduler.model.anno.Scheduler;
|
||||
import com.zfoo.scheduler.model.vo.SchedulerDefinition;
|
||||
import com.zfoo.scheduler.util.TimeUtils;
|
||||
import io.netty.util.concurrent.FastThreadLocalThread;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
/**
|
||||
* @author jaysunxiao
|
||||
* @version 3.0
|
||||
*/
|
||||
public class SchedulerManager implements ISchedulerManager {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(SchedulerManager.class);
|
||||
|
||||
private static final SchedulerManager INSTANCE = new SchedulerManager();
|
||||
|
||||
private static final List<SchedulerDefinition> schedulerDefList = new CopyOnWriteArrayList<>();
|
||||
|
||||
/**
|
||||
* 上一次trigger触发时间
|
||||
*/
|
||||
private static long lastTriggerTimestamp = 0;
|
||||
|
||||
|
||||
/**
|
||||
* 在scheduler中,最小的triggerTimestamp
|
||||
*/
|
||||
private static long minSchedulerTriggerTimestamp = 0;
|
||||
|
||||
|
||||
private static final long TRIGGER_MILLIS_INTERVAL = TimeUtils.MILLIS_PER_SECOND;
|
||||
private static final ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor(new SchedulerThreadFactory());
|
||||
|
||||
|
||||
private static class SchedulerThreadFactory implements ThreadFactory {
|
||||
private static final AtomicInteger poolNumber = new AtomicInteger(1);
|
||||
private final ThreadGroup group;
|
||||
private final AtomicInteger threadNumber = new AtomicInteger(1);
|
||||
private final String namePrefix;
|
||||
|
||||
// scheduler-p1-t1 = scheduler-pool-1-thread-1
|
||||
SchedulerThreadFactory() {
|
||||
var s = System.getSecurityManager();
|
||||
group = (s != null) ? s.getThreadGroup() : Thread.currentThread().getThreadGroup();
|
||||
namePrefix = "scheduler-p" + poolNumber.getAndIncrement() + "-t";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Thread newThread(Runnable runnable) {
|
||||
var t = new FastThreadLocalThread(group, runnable, namePrefix + threadNumber.getAndIncrement(), 0);
|
||||
t.setDaemon(false);
|
||||
t.setPriority(Thread.NORM_PRIORITY);
|
||||
t.setUncaughtExceptionHandler((thread, e) -> {
|
||||
logger.error(thread.toString(), e);
|
||||
});
|
||||
return t;
|
||||
}
|
||||
}
|
||||
|
||||
static {
|
||||
executor.scheduleAtFixedRate(() -> {
|
||||
try {
|
||||
triggerPerSecond();
|
||||
} catch (Exception e) {
|
||||
logger.error("scheduler triggers an error.", e);
|
||||
}
|
||||
}, TimeUtils.MILLIS_PER_SECOND, TRIGGER_MILLIS_INTERVAL, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
public static SchedulerManager getInstance() {
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
private SchedulerManager() {
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 每一秒执行一次,如果这个任务执行时间过长超过,比如10秒,执行完成后,不会再执行10次
|
||||
*/
|
||||
private static void triggerPerSecond() {
|
||||
var timestamp = TimeUtils.currentTimeMillis();
|
||||
|
||||
if (CollectionUtils.isEmpty(schedulerDefList)) {
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
有人向前调整过机器时间,重新计算scheduler里的triggerTimestamp
|
||||
*/
|
||||
// var diff = timestamp - lastTriggerTimestamp;
|
||||
if (timestamp < lastTriggerTimestamp) {
|
||||
for (SchedulerDefinition schedulerDef : schedulerDefList) {
|
||||
var nextTriggerTimestamp = TimeUtils.getNextTimestampByCronExpression(schedulerDef.getCronExpression(), timestamp);
|
||||
schedulerDef.setTriggerTimestamp(nextTriggerTimestamp);
|
||||
}
|
||||
minSchedulerTriggerTimestamp = minSchedulerTriggerTimestamp();
|
||||
}
|
||||
|
||||
// diff > 0, 没有人调整时间或者有人向后调整过机器时间,可以忽略,因为向后调整时间时间戳一定会大于triggerTimestamp,所以一定会触发
|
||||
lastTriggerTimestamp = timestamp;
|
||||
|
||||
// 如果minSchedulerTriggerTimestamp大于timestamp,说明没有可执行的scheduler
|
||||
if (timestamp < minSchedulerTriggerTimestamp) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (var schedulerDef : schedulerDefList) {
|
||||
if (timestamp >= schedulerDef.getTriggerTimestamp()) {
|
||||
// 到达触发时间,则执行runnable方法
|
||||
schedulerDef.getScheduler().invoke();
|
||||
// 重新设置下一次的触发时间戳
|
||||
var nextTriggerTimestamp = TimeUtils.getNextTimestampByCronExpression(schedulerDef.getCronExpression(), timestamp);
|
||||
schedulerDef.setTriggerTimestamp(nextTriggerTimestamp);
|
||||
}
|
||||
}
|
||||
minSchedulerTriggerTimestamp = minSchedulerTriggerTimestamp();
|
||||
}
|
||||
|
||||
public void registerScheduler(Object bean) {
|
||||
try {
|
||||
var methods = ReflectionUtils.getMethodsByAnnoInPOJOClass(bean.getClass(), Scheduler.class);
|
||||
for (var method : methods) {
|
||||
var scheduler = 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 schedulerDef = SchedulerDefinition.valueOf(scheduler.cron(), bean, method);
|
||||
schedulerDefList.add(schedulerDef);
|
||||
minSchedulerTriggerTimestamp = minSchedulerTriggerTimestamp();
|
||||
}
|
||||
} catch (Throwable throwable) {
|
||||
throw new RuntimeException(throwable);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void scheduleAtFixedRate(Runnable runnable, long period, TimeUnit unit) {
|
||||
if (SchedulerContext.isStop()) {
|
||||
return;
|
||||
}
|
||||
|
||||
executor.scheduleAtFixedRate(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
runnable.run();
|
||||
} catch (Exception e) {
|
||||
logger.error("scheduleAtFixedRate未知exception异常", e);
|
||||
} catch (Throwable t) {
|
||||
logger.error("scheduleAtFixedRate未知error异常", t);
|
||||
}
|
||||
}
|
||||
}, 0, period, unit);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void schedule(Runnable runnable, long delay, TimeUnit unit) {
|
||||
if (SchedulerContext.isStop()) {
|
||||
return;
|
||||
}
|
||||
|
||||
executor.schedule(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
runnable.run();
|
||||
} catch (Exception e) {
|
||||
logger.error("schedule未知exception异常", e);
|
||||
} catch (Throwable t) {
|
||||
logger.error("schedule未知error异常", t);
|
||||
}
|
||||
}
|
||||
}, delay, unit);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void scheduleCron(Runnable runnable, String cron) {
|
||||
if (SchedulerContext.isStop()) {
|
||||
return;
|
||||
}
|
||||
|
||||
schedulerDefList.add(SchedulerDefinition.valueOf(cron, runnable));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* 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.model.anno;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* @author jaysunxiao
|
||||
* @version 3.0
|
||||
*/
|
||||
@Documented
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ElementType.METHOD})
|
||||
public @interface Scheduler {
|
||||
|
||||
String cron();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* 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.model.vo;
|
||||
|
||||
import com.zfoo.protocol.util.StringUtils;
|
||||
import com.zfoo.scheduler.schema.NamespaceHandler;
|
||||
import com.zfoo.util.security.IdUtils;
|
||||
import javassist.*;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
|
||||
/**
|
||||
* @author jaysunxiao
|
||||
* @version 3.0
|
||||
*/
|
||||
public abstract class EnhanceUtils {
|
||||
|
||||
static {
|
||||
// 适配Tomcat,因为Tomcat不是用的默认的类加载器,而Javaassist用的是默认的加载器
|
||||
var classArray = new Class<?>[]{
|
||||
IScheduler.class
|
||||
};
|
||||
|
||||
var classPool = ClassPool.getDefault();
|
||||
|
||||
for (var clazz : classArray) {
|
||||
if (classPool.find(clazz.getCanonicalName()) == null) {
|
||||
ClassClassPath classPath = new ClassClassPath(clazz);
|
||||
classPool.insertClassPath(classPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static IScheduler createScheduler(ReflectScheduler reflectScheduler) throws NotFoundException, CannotCompileException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
|
||||
var classPool = ClassPool.getDefault();
|
||||
|
||||
Object bean = reflectScheduler.getBean();
|
||||
Method method = reflectScheduler.getMethod();
|
||||
|
||||
// 定义类名称
|
||||
CtClass enhanceClazz = classPool.makeClass(EnhanceUtils.class.getCanonicalName() + StringUtils.capitalize(NamespaceHandler.SCHEDULER) + IdUtils.getLocalIntId());
|
||||
enhanceClazz.addInterface(classPool.get(IScheduler.class.getCanonicalName()));
|
||||
|
||||
// 定义类中的一个成员
|
||||
CtField field = new CtField(classPool.get(bean.getClass().getCanonicalName()), "bean", enhanceClazz);
|
||||
field.setModifiers(Modifier.PRIVATE);
|
||||
enhanceClazz.addField(field);
|
||||
|
||||
// 定义类的构造器
|
||||
CtConstructor constructor = new CtConstructor(classPool.get(new String[]{bean.getClass().getCanonicalName()}), enhanceClazz);
|
||||
constructor.setBody("{this.bean=$1;}");
|
||||
constructor.setModifiers(Modifier.PUBLIC);
|
||||
enhanceClazz.addConstructor(constructor);
|
||||
|
||||
// 定义类实现的接口方法
|
||||
CtMethod invokeMethod = new CtMethod(classPool.get(void.class.getCanonicalName()), "invoke", null, enhanceClazz);
|
||||
invokeMethod.setModifiers(Modifier.PUBLIC + Modifier.FINAL);
|
||||
String invokeMethodBody = "{this.bean." + method.getName() + "();}";
|
||||
invokeMethod.setBody(invokeMethodBody);
|
||||
enhanceClazz.addMethod(invokeMethod);
|
||||
|
||||
// 释放缓存
|
||||
enhanceClazz.detach();
|
||||
|
||||
Class<?> resultClazz = enhanceClazz.toClass(IScheduler.class);
|
||||
Constructor<?> resultConstructor = resultClazz.getConstructor(bean.getClass());
|
||||
return (IScheduler) resultConstructor.newInstance(bean);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* 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.model.vo;
|
||||
|
||||
/**
|
||||
* @author jaysunxiao
|
||||
* @version 3.0
|
||||
*/
|
||||
public interface IScheduler {
|
||||
|
||||
|
||||
void invoke();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* 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.model.vo;
|
||||
|
||||
import com.zfoo.protocol.util.ReflectionUtils;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
/**
|
||||
* @author jaysunxiao
|
||||
* @version 3.0
|
||||
*/
|
||||
public class ReflectScheduler implements IScheduler {
|
||||
|
||||
private Object bean;
|
||||
|
||||
private Method method;
|
||||
|
||||
public static ReflectScheduler valueOf(Object bean, Method method) {
|
||||
var scheduler = new ReflectScheduler();
|
||||
scheduler.bean = bean;
|
||||
scheduler.method = method;
|
||||
return scheduler;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invoke() {
|
||||
ReflectionUtils.invokeMethod(bean, method);
|
||||
}
|
||||
|
||||
public Object getBean() {
|
||||
return bean;
|
||||
}
|
||||
|
||||
public Method getMethod() {
|
||||
return method;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* 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.model.vo;
|
||||
|
||||
/**
|
||||
* @author jaysunxiao
|
||||
* @version 3.0
|
||||
*/
|
||||
public class RunnableScheduler implements IScheduler {
|
||||
|
||||
private Runnable runnable;
|
||||
|
||||
public static RunnableScheduler valueOf(Runnable runnable) {
|
||||
var scheduler = new RunnableScheduler();
|
||||
scheduler.runnable = runnable;
|
||||
return scheduler;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invoke() {
|
||||
runnable.run();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* 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.model.vo;
|
||||
|
||||
import com.zfoo.protocol.util.ReflectionUtils;
|
||||
import com.zfoo.scheduler.util.TimeUtils;
|
||||
import javassist.CannotCompileException;
|
||||
import javassist.NotFoundException;
|
||||
import org.springframework.scheduling.support.CronExpression;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
/**
|
||||
* @author jaysunxiao
|
||||
* @version 3.0
|
||||
*/
|
||||
public class SchedulerDefinition {
|
||||
|
||||
private CronExpression cronExpression;
|
||||
|
||||
private IScheduler scheduler;
|
||||
|
||||
/**
|
||||
* 触发时间戳,只要当前时间戳大于这个触发事件戳都视为可以触发
|
||||
*/
|
||||
private long triggerTimestamp;
|
||||
|
||||
public static SchedulerDefinition valueOf(String cron, Object bean, Method method) throws NoSuchMethodException, IllegalAccessException, InstantiationException, CannotCompileException, NotFoundException, InvocationTargetException {
|
||||
var schedulerDef = new SchedulerDefinition();
|
||||
var cronExpression = CronExpression.parse(cron);
|
||||
schedulerDef.cronExpression = cronExpression;
|
||||
// 字节码增强,避免反射
|
||||
schedulerDef.scheduler = EnhanceUtils.createScheduler(ReflectScheduler.valueOf(bean, method));
|
||||
schedulerDef.triggerTimestamp = TimeUtils.getNextTimestampByCronExpression(cronExpression, TimeUtils.currentTimeMillis());
|
||||
ReflectionUtils.makeAccessible(method);
|
||||
return schedulerDef;
|
||||
}
|
||||
|
||||
public static SchedulerDefinition valueOf(String cron, Runnable runnable) {
|
||||
var schedulerDef = new SchedulerDefinition();
|
||||
var cronExpression = CronExpression.parse(cron);
|
||||
schedulerDef.cronExpression = cronExpression;
|
||||
schedulerDef.scheduler = RunnableScheduler.valueOf(runnable);
|
||||
schedulerDef.triggerTimestamp = TimeUtils.getNextTimestampByCronExpression(cronExpression, TimeUtils.currentTimeMillis());
|
||||
return schedulerDef;
|
||||
}
|
||||
|
||||
public CronExpression getCronExpression() {
|
||||
return cronExpression;
|
||||
}
|
||||
|
||||
public void setCronExpression(CronExpression cronExpression) {
|
||||
this.cronExpression = cronExpression;
|
||||
}
|
||||
|
||||
public IScheduler getScheduler() {
|
||||
return scheduler;
|
||||
}
|
||||
|
||||
public void setScheduler(IScheduler scheduler) {
|
||||
this.scheduler = scheduler;
|
||||
}
|
||||
|
||||
public long getTriggerTimestamp() {
|
||||
return triggerTimestamp;
|
||||
}
|
||||
|
||||
public void setTriggerTimestamp(long triggerTimestamp) {
|
||||
this.triggerTimestamp = triggerTimestamp;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* 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 org.springframework.beans.factory.xml.NamespaceHandlerSupport;
|
||||
|
||||
/**
|
||||
* @author jaysunxiao
|
||||
* @version 3.0
|
||||
*/
|
||||
public class NamespaceHandler extends NamespaceHandlerSupport {
|
||||
|
||||
public static final String SCHEDULER = "scheduler";
|
||||
|
||||
@Override
|
||||
public void init() {
|
||||
registerBeanDefinitionParser(SCHEDULER, new SchedulerDefinitionParser());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* 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.util.StringUtils;
|
||||
import com.zfoo.scheduler.SchedulerContext;
|
||||
import org.springframework.beans.factory.support.AbstractBeanDefinition;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.beans.factory.xml.BeanDefinitionParser;
|
||||
import org.springframework.beans.factory.xml.ParserContext;
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
/**
|
||||
* @author jaysunxiao
|
||||
* @version 3.0
|
||||
*/
|
||||
public class SchedulerDefinitionParser implements BeanDefinitionParser {
|
||||
|
||||
private final String SCHEDULER_ID = "id";
|
||||
|
||||
@Override
|
||||
public AbstractBeanDefinition parse(Element element, ParserContext parserContext) {
|
||||
Class<?> clazz;
|
||||
String name;
|
||||
BeanDefinitionBuilder builder;
|
||||
|
||||
// 注册SchedulerSpringContext
|
||||
clazz = SchedulerContext.class;
|
||||
name = StringUtils.uncapitalize(clazz.getName());
|
||||
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());
|
||||
|
||||
// 注册SchedulerManager
|
||||
String schedulerId = element.getAttribute(SCHEDULER_ID);
|
||||
return builder.getBeanDefinition();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* 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.scheduler.SchedulerContext;
|
||||
import com.zfoo.scheduler.manager.SchedulerManager;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.config.BeanPostProcessor;
|
||||
|
||||
/**
|
||||
* @author jaysunxiao
|
||||
* @version 3.0
|
||||
*/
|
||||
public class SchedulerRegisterProcessor implements BeanPostProcessor {
|
||||
|
||||
|
||||
@Override
|
||||
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
|
||||
if (SchedulerContext.getSchedulerContext() == null) {
|
||||
return bean;
|
||||
}
|
||||
SchedulerManager schedulerManager = (SchedulerManager) SchedulerContext.getSchedulerManager();
|
||||
schedulerManager.registerScheduler(bean);
|
||||
return bean;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,373 @@
|
||||
/*
|
||||
* 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.util;
|
||||
|
||||
import com.zfoo.scheduler.SchedulerContext;
|
||||
import com.zfoo.scheduler.manager.SchedulerManager;
|
||||
import io.netty.util.concurrent.FastThreadLocal;
|
||||
import org.springframework.scheduling.support.CronExpression;
|
||||
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.time.*;
|
||||
import java.time.temporal.TemporalAdjusters;
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.TimeZone;
|
||||
|
||||
/**
|
||||
* @author jaysunxiao
|
||||
* @version 3.0
|
||||
*/
|
||||
public abstract class TimeUtils {
|
||||
|
||||
private static long timestamp = System.currentTimeMillis();
|
||||
|
||||
// 一秒钟对应的纳秒数
|
||||
public static final long NANO_PER_SECOND = 1_000_000_000;
|
||||
// 一秒钟对应的毫秒数
|
||||
public static final long MILLIS_PER_SECOND = 1 * 1000;
|
||||
// 一分钟对应的毫秒数
|
||||
public static final long MILLIS_PER_MINUTE = 1 * 60 * MILLIS_PER_SECOND;
|
||||
// 一个小时对应的毫秒数
|
||||
public static final long MILLIS_PER_HOUR = 1 * 60 * MILLIS_PER_MINUTE;
|
||||
// 一天对应的毫秒数
|
||||
public static final long MILLIS_PER_DAY = 1 * 24 * MILLIS_PER_HOUR;
|
||||
// 一周对应的毫秒数
|
||||
public static final long MILLIS_PER_WEEK = 1 * 7 * MILLIS_PER_HOUR;
|
||||
// 默认的时区
|
||||
public static final TimeZone DEFAULT_TIME_ZONE = TimeZone.getDefault();
|
||||
// 默认的时区Id
|
||||
public static final ZoneId DEFAULT_ZONE_ID = TimeZone.getDefault().toZoneId();
|
||||
|
||||
|
||||
// 统一的时间格式模板
|
||||
public static final String DATE_FORMAT_TEMPLATE = "yyyy-MM-dd HH:mm:ss";
|
||||
// 简单的时间格式模板
|
||||
public static final String SIMPLE_DATE_FORMAT_TEMPLATE = "yyyyMMddHHmmss";
|
||||
// 统一的时间格式模板
|
||||
public static final String DATE_FORMAT_TEMPLATE_FOR_DAY = "yyyy-MM-dd";
|
||||
|
||||
private static final FastThreadLocal<SimpleDateFormat> DATE_FORMAT = new FastThreadLocal<SimpleDateFormat>() {
|
||||
@Override
|
||||
protected SimpleDateFormat initialValue() {
|
||||
return new SimpleDateFormat(DATE_FORMAT_TEMPLATE);
|
||||
}
|
||||
};
|
||||
|
||||
private static final FastThreadLocal<SimpleDateFormat> SIMPLE_DATE_FORMAT = new FastThreadLocal<SimpleDateFormat>() {
|
||||
@Override
|
||||
protected SimpleDateFormat initialValue() {
|
||||
return new SimpleDateFormat(SIMPLE_DATE_FORMAT_TEMPLATE);
|
||||
}
|
||||
};
|
||||
|
||||
private static final FastThreadLocal<SimpleDateFormat> DATE_FORMAT_FOR_DAY = new FastThreadLocal<SimpleDateFormat>() {
|
||||
@Override
|
||||
protected SimpleDateFormat initialValue() {
|
||||
return new SimpleDateFormat(DATE_FORMAT_TEMPLATE_FOR_DAY);
|
||||
}
|
||||
};
|
||||
|
||||
static {
|
||||
SchedulerContext.getSchedulerManager();
|
||||
SchedulerManager.getInstance();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取精确的时间戳
|
||||
*/
|
||||
public static long currentTimeMillis() {
|
||||
timestamp = System.currentTimeMillis();
|
||||
return timestamp;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取最多只有一秒延迟的粗略时间戳,适用于对时间精度要求不高的场景,最多只有一秒误差
|
||||
* <p>
|
||||
* 比System.currentTimeMillis()的性能高10倍
|
||||
*/
|
||||
public static long now() {
|
||||
return timestamp;
|
||||
}
|
||||
|
||||
// --------------------------------------日期格式--------------------------------------
|
||||
|
||||
/**
|
||||
* 把日期字符串转换成Date对象,统一的日期模板要遵循DATE_FORMAT_TEMPLATE="yyyy-MM-dd HH:mm:ss"
|
||||
*
|
||||
* @param dateString 日期字符串,如:2018-02-12 10:12:50
|
||||
* @return <code>Date</code>
|
||||
* @throws ParseException 解析异常
|
||||
*/
|
||||
public static Date stringToDate(String dateString) throws ParseException {
|
||||
return DATE_FORMAT.get().parse(dateString);
|
||||
}
|
||||
|
||||
/**
|
||||
* 把long类型的时间戳转换成字符串格式的时间
|
||||
*
|
||||
* @param time 时间戳
|
||||
* @return yyyy-MM-dd HH:mm:ss
|
||||
*/
|
||||
public static String timeToString(long time) {
|
||||
return dateToString(new Date(time));
|
||||
}
|
||||
|
||||
public static String dateToString(Date date) {
|
||||
return DATE_FORMAT.get().format(date);
|
||||
}
|
||||
|
||||
public static String simpleDateString() {
|
||||
return SIMPLE_DATE_FORMAT.get().format(new Date(now()));
|
||||
}
|
||||
|
||||
public static Date dayStringToDate(String dateString) throws ParseException {
|
||||
return DATE_FORMAT_FOR_DAY.get().parse(dateString);
|
||||
}
|
||||
// --------------------------------------日期判断--------------------------------------
|
||||
|
||||
/**
|
||||
* <p>Checks if two date objects are on the same day ignoring time.</p>
|
||||
*
|
||||
* <p>28 Mar 2002 13:45 and 28 Mar 2002 06:01 would return true.
|
||||
* 28 Mar 2002 13:45 and 12 Mar 2002 13:45 would return false.
|
||||
* </p>
|
||||
*
|
||||
* @param date1 the first date, not altered, not null
|
||||
* @param date2 the second date, not altered, not null
|
||||
* @return true if they represent the same day
|
||||
* @throws IllegalArgumentException if either date is <code>null</code>
|
||||
*/
|
||||
public static boolean isSameDay(Date date1, Date date2) {
|
||||
if (date1 == null || date2 == null) {
|
||||
throw new IllegalArgumentException("The date must not be null");
|
||||
}
|
||||
var cal1 = Calendar.getInstance();
|
||||
cal1.setTime(date1);
|
||||
var cal2 = Calendar.getInstance();
|
||||
cal2.setTime(date2);
|
||||
return isSameDay(cal1, cal2);
|
||||
}
|
||||
|
||||
public static boolean isSameDay(long time1, long time2) {
|
||||
var cal1 = Calendar.getInstance();
|
||||
cal1.setTimeInMillis(time1);
|
||||
var cal2 = Calendar.getInstance();
|
||||
cal2.setTimeInMillis(time2);
|
||||
return isSameDay(cal1, cal2);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Checks if two calendar objects are on the same day ignoring time.</p>
|
||||
*
|
||||
* <p>28 Mar 2002 13:45 and 28 Mar 2002 06:01 would return true.
|
||||
* 28 Mar 2002 13:45 and 12 Mar 2002 13:45 would return false.
|
||||
* </p>
|
||||
*
|
||||
* @param cal1 the first calendar, not altered, not null
|
||||
* @param cal2 the second calendar, not altered, not null
|
||||
* @return true if they represent the same day
|
||||
* @throws IllegalArgumentException if either calendar is <code>null</code>
|
||||
*/
|
||||
public static boolean isSameDay(Calendar cal1, Calendar cal2) {
|
||||
if (cal1 == null || cal2 == null) {
|
||||
throw new IllegalArgumentException("The date must not be null");
|
||||
}
|
||||
return cal1.get(Calendar.ERA) == cal2.get(Calendar.ERA) &&
|
||||
cal1.get(Calendar.YEAR) == cal2.get(Calendar.YEAR) &&
|
||||
cal1.get(Calendar.DAY_OF_YEAR) == cal2.get(Calendar.DAY_OF_YEAR);
|
||||
}
|
||||
|
||||
public static String dateFormatForDayString(long time) {
|
||||
return DATE_FORMAT_FOR_DAY.get().format(new Date(time));
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断两个日期是否是同一周,设置周一为一周的第一天
|
||||
* <p>
|
||||
* 2004-12-25”是星期六,也就是说它是2004年中第52周的星期六,那么“2004-12-26”到底是2004年的第几周哪,java中经测试取得的它的Week值是1,
|
||||
* 那么也就是说它被看作2005年的第一周了,这个处理是比较好的。可以用来判断“2004-12-26”和“2005-1-1”是同一周。
|
||||
*/
|
||||
public static boolean isSameWeek(long time1, long time2) {
|
||||
Calendar cal1 = Calendar.getInstance();
|
||||
Calendar cal2 = Calendar.getInstance();
|
||||
cal1.setTimeInMillis(time1);
|
||||
cal2.setTimeInMillis(time2);
|
||||
cal1.setFirstDayOfWeek(Calendar.MONDAY);
|
||||
cal2.setFirstDayOfWeek(Calendar.MONDAY);
|
||||
|
||||
int yearDiff = cal1.get(Calendar.YEAR) - cal2.get(Calendar.YEAR);
|
||||
if (yearDiff == 0 && cal1.get(Calendar.WEEK_OF_YEAR) == cal2.get(Calendar.WEEK_OF_YEAR)) {
|
||||
// yearDiff==0,说明是同一年
|
||||
if (cal1.get(Calendar.WEEK_OF_YEAR) == cal2.get(Calendar.WEEK_OF_YEAR)) {
|
||||
return true;
|
||||
}
|
||||
} else if (yearDiff == 1 && cal2.get(Calendar.MONTH) == 11) {
|
||||
//yearDiff==1,说明cal比cal2大一年;java的一月用"0"标识,那么12月用"11"
|
||||
if (cal1.get(Calendar.WEEK_OF_YEAR) == cal2.get(Calendar.WEEK_OF_YEAR)) {
|
||||
return true;
|
||||
}
|
||||
} else if (yearDiff == -1 && cal1.get(Calendar.MONTH) == 11) {
|
||||
//yearDiff==-1,说明cal比cal2小一年
|
||||
if (cal1.get(Calendar.WEEK_OF_YEAR) == cal2.get(Calendar.WEEK_OF_YEAR)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static boolean isSameMonth(long time1, long time2) {
|
||||
Calendar cal1 = Calendar.getInstance();
|
||||
Calendar cal2 = Calendar.getInstance();
|
||||
cal1.setTimeInMillis(time1);
|
||||
cal2.setTimeInMillis(time2);
|
||||
return cal1.get(Calendar.ERA) == cal2.get(Calendar.ERA)
|
||||
&& cal1.get(Calendar.YEAR) == cal2.get(Calendar.YEAR) && cal1.get(Calendar.MONTH) == cal2.get(Calendar.MONTH);
|
||||
}
|
||||
|
||||
// --------------------------------------获取相关时间戳--------------------------------------
|
||||
|
||||
|
||||
/**
|
||||
* 获取给定时间戳对应的日期的0点时间戳
|
||||
*/
|
||||
public static long getZeroTimeOfDay(long time) {
|
||||
Calendar cal = Calendar.getInstance();
|
||||
cal.setTimeInMillis(time);
|
||||
cal.set(Calendar.HOUR_OF_DAY, 0);
|
||||
cal.set(Calendar.MINUTE, 0);
|
||||
cal.set(Calendar.SECOND, 0);
|
||||
cal.set(Calendar.MILLISECOND, 0);
|
||||
return cal.getTimeInMillis();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取给定时间戳对应的日期的最后时刻时间戳
|
||||
*/
|
||||
public static long getLastTimeOfDay(long time) {
|
||||
Calendar cal = Calendar.getInstance();
|
||||
cal.setTimeInMillis(time);
|
||||
cal.set(Calendar.HOUR_OF_DAY, 23);
|
||||
cal.set(Calendar.MINUTE, 59);
|
||||
cal.set(Calendar.SECOND, 59);
|
||||
cal.set(Calendar.MILLISECOND, 999);
|
||||
return cal.getTimeInMillis();
|
||||
}
|
||||
|
||||
// 获取给定时间戳对应的日期的昨天这个时刻的时间戳
|
||||
public static long getYesterdayTimeOfDay(long time) {
|
||||
Calendar cal = Calendar.getInstance();
|
||||
cal.setTimeInMillis(time);
|
||||
cal.add(Calendar.DATE, -1);
|
||||
return cal.getTimeInMillis();
|
||||
}
|
||||
|
||||
// 获取给定时间戳对应小时的起始时间戳
|
||||
public static long getStartTimeOfHour(long time) {
|
||||
var localDateTime = TimeUtils.timestampToLocalDateTime(time);
|
||||
var monday = localDateTime.withMinute(0).withSecond(0).withNano(0);
|
||||
return TimeUtils.localDateTimeToTimestamp(monday);
|
||||
}
|
||||
|
||||
// 获取给定时间戳对应周的第一天的起始时间戳
|
||||
public static long getStartTimeOfWeek(long time) {
|
||||
var localDateTime = TimeUtils.timestampToLocalDateTime(time);
|
||||
var monday = localDateTime.with(TemporalAdjusters.previous(DayOfWeek.SUNDAY)).plusDays(1).withHour(0).withMinute(0).withSecond(0).withNano(0);
|
||||
return TimeUtils.localDateTimeToTimestamp(monday);
|
||||
}
|
||||
|
||||
// 获取给定时间戳对应月的第一天的起始时间戳
|
||||
public static long getStartTimeOfMonth(long time) {
|
||||
var localDateTime = TimeUtils.timestampToLocalDateTime(time);
|
||||
var oneOfMonth = localDateTime.with(TemporalAdjusters.firstDayOfMonth()).withHour(0).withMinute(0).withSecond(0).withNano(0);
|
||||
return TimeUtils.localDateTimeToTimestamp(oneOfMonth);
|
||||
}
|
||||
|
||||
// 获取给定时间戳对应月的最后一天的结束时间戳
|
||||
public static long getEndTimeOfMonth(long time) {
|
||||
var localDateTime = TimeUtils.timestampToLocalDateTime(time);
|
||||
var oneOfMonth = localDateTime.with(TemporalAdjusters.lastDayOfMonth()).withHour(23).withMinute(59).withSecond(59).withNano(0);
|
||||
return TimeUtils.localDateTimeToTimestamp(oneOfMonth);
|
||||
}
|
||||
|
||||
//获取上一个月月初的时间
|
||||
public static long getLastMonthStart(long time) {
|
||||
var localDateTime = TimeUtils.timestampToLocalDateTime(time);
|
||||
var lastMonth = localDateTime.minusMonths(1);
|
||||
LocalDateTime with = lastMonth.with(TemporalAdjusters.firstDayOfMonth()).withHour(0).withMinute(0).withSecond(0).withNano(0);
|
||||
return TimeUtils.localDateTimeToTimestamp(with);
|
||||
}
|
||||
|
||||
//获取上一个月月末的时间
|
||||
public static long getLastMonthEnd(long time) {
|
||||
var localDateTime = TimeUtils.timestampToLocalDateTime(time);
|
||||
var lastMonth = localDateTime.minusMonths(1);
|
||||
LocalDateTime with = lastMonth.with(TemporalAdjusters.lastDayOfMonth()).withHour(23).withMinute(59).withSecond(59).withNano(0);
|
||||
return TimeUtils.localDateTimeToTimestamp(with);
|
||||
}
|
||||
|
||||
//获取下一个月月初的时间
|
||||
public static long getNextMonthStart(long time) {
|
||||
var localDateTime = TimeUtils.timestampToLocalDateTime(time);
|
||||
var lastMonth = localDateTime.plusMonths(1);
|
||||
LocalDateTime with = lastMonth.with(TemporalAdjusters.firstDayOfMonth()).withHour(0).withMinute(0).withSecond(0).withNano(0);
|
||||
return TimeUtils.localDateTimeToTimestamp(with);
|
||||
}
|
||||
|
||||
// 获取下一个月月末的时间
|
||||
public static long getNextMonthEnd(long time) {
|
||||
var localDateTime = TimeUtils.timestampToLocalDateTime(time);
|
||||
var lastMonth = localDateTime.plusMonths(1);
|
||||
LocalDateTime with = lastMonth.with(TemporalAdjusters.lastDayOfMonth()).withHour(23).withMinute(59).withSecond(59).withNano(0);
|
||||
return TimeUtils.localDateTimeToTimestamp(with);
|
||||
}
|
||||
|
||||
/**
|
||||
* LocalDateTime转毫秒时间戳
|
||||
*
|
||||
* @param localDateTime LocalDateTime
|
||||
* @return 时间戳
|
||||
*/
|
||||
public static Long localDateTimeToTimestamp(LocalDateTime localDateTime) {
|
||||
var zoneId = ZoneId.systemDefault();
|
||||
var instant = localDateTime.atZone(zoneId).toInstant();
|
||||
return instant.toEpochMilli();
|
||||
}
|
||||
|
||||
/**
|
||||
* 时间戳转LocalDateTime
|
||||
*
|
||||
* @param timestamp 时间戳
|
||||
* @return LocalDateTime
|
||||
*/
|
||||
public static LocalDateTime timestampToLocalDateTime(long timestamp) {
|
||||
var instant = Instant.ofEpochMilli(timestamp);
|
||||
var zone = ZoneId.systemDefault();
|
||||
return LocalDateTime.ofInstant(instant, zone);
|
||||
}
|
||||
|
||||
// --------------------------------------cron表达式--------------------------------------
|
||||
public static long getNextTimestampByCronExpression(CronExpression expression, long currentTimestamp) {
|
||||
var next = expression.next(ZonedDateTime.ofInstant(Instant.ofEpochMilli(currentTimestamp), DEFAULT_ZONE_ID));
|
||||
|
||||
if (next == null) {
|
||||
return Long.MAX_VALUE;
|
||||
}
|
||||
|
||||
return next.toInstant().toEpochMilli();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
http\://www.zfoo.com/schema/scheduler=com.zfoo.scheduler.schema.NamespaceHandler
|
||||
@@ -0,0 +1 @@
|
||||
http\://www.zfoo.com/schema/scheduler-1.0.xsd=scheduler-1.0.xsd
|
||||
@@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no" ?>
|
||||
|
||||
|
||||
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
|
||||
|
||||
xmlns="http://www.zfoo.com/schema/scheduler"
|
||||
targetNamespace="http://www.zfoo.com/schema/scheduler"
|
||||
|
||||
elementFormDefault="qualified"
|
||||
attributeFormDefault="unqualified">
|
||||
|
||||
<xsd:element name="scheduler">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="id" type="xsd:string" use="required"/>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
|
||||
|
||||
</xsd:schema>
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import com.zfoo.util.ThreadUtils;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import org.springframework.context.support.ClassPathXmlApplicationContext;
|
||||
|
||||
/**
|
||||
* cron(译为克龙)代表100万年,是英文单词中最大的时间单位。
|
||||
* google(译为古戈尔)代表10的100次方,足够穷尽宇宙万物
|
||||
*
|
||||
* @author jaysunxiao
|
||||
* @version 3.0
|
||||
*/
|
||||
|
||||
@Ignore
|
||||
public class ApplicationTest {
|
||||
|
||||
@Test
|
||||
public void startSchedulerTest() {
|
||||
// 加载配置文件,配置文件中必须引入scheduler
|
||||
var context = new ClassPathXmlApplicationContext("application.xml");
|
||||
|
||||
ThreadUtils.sleep(Long.MAX_VALUE);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import com.zfoo.scheduler.model.anno.Scheduler;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* @author jaysunxiao
|
||||
* @version 3.0
|
||||
*/
|
||||
@Component
|
||||
public class SchedulerController {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(SchedulerController.class);
|
||||
|
||||
@Scheduler(cron = "0/5 * * * * ?")
|
||||
public void cronScheduler1() {
|
||||
logger.info("scheduler1 每5秒时间调度任务");
|
||||
}
|
||||
|
||||
@Scheduler(cron = "0,10,20,40 * * * * ?")
|
||||
public void cronScheduler2() {
|
||||
logger.info("scheduler2 每分钟的10秒,20秒,40秒调度任务");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
/*
|
||||
* 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.util;
|
||||
|
||||
import com.zfoo.util.ThreadUtils;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.time.DayOfWeek;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.LocalTime;
|
||||
import java.time.temporal.TemporalAdjusters;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* @author jaysunxiao
|
||||
* @version 3.0
|
||||
*/
|
||||
public class TimeUtilsTest {
|
||||
|
||||
@Test
|
||||
public void testLocalDate() {
|
||||
// 取当前日期:
|
||||
LocalDate today = LocalDate.now(); // -> 2014-12-24
|
||||
// 根据年月日取日期:
|
||||
LocalDate crischristmas = LocalDate.of(2014, 12, 25); // -> 2014-12-25
|
||||
// 根据字符串取:
|
||||
LocalDate endOfFeb = LocalDate.parse("2014-02-28"); // 严格按照ISO yyyy-MM-dd验证,02写成2都不行,当然也有一个重载方法允许自己定义格式
|
||||
LocalDate.parse("2014-02-26"); // 无效日期无法通过:DateTimeParseException: Invalid date
|
||||
// 取本月第1天:
|
||||
LocalDate firstDayOfThisMonth = today.with(TemporalAdjusters.firstDayOfMonth()); // 2017-03-01
|
||||
// 取本月第2天:
|
||||
LocalDate secondDayOfThisMonth = today.withDayOfMonth(2); // 2017-03-02
|
||||
// 取本月最后一天,再也不用计算是28,29,30还是31:
|
||||
LocalDate lastDayOfThisMonth = today.with(TemporalAdjusters.lastDayOfMonth()); // 2017-12-31
|
||||
// 取下一天:
|
||||
LocalDate firstDayOf2015 = lastDayOfThisMonth.plusDays(1); // 变成了2018-01-01
|
||||
// 取2017年1月第一个周一,用Calendar要死掉很多脑细胞:
|
||||
LocalDate firstMondayOf2015 = LocalDate.parse("2017-01-01").with(TemporalAdjusters.firstInMonth(DayOfWeek.MONDAY)); // 2017-01-02
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLocalTime() {
|
||||
System.out.println(Long.MAX_VALUE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLocalDateTime() {
|
||||
LocalDate date = LocalDate.of(2018, 12, 4);
|
||||
LocalTime time = LocalTime.of(0, 0, 0);
|
||||
LocalDateTime localDateTime = LocalDateTime.of(date, time);
|
||||
System.out.println(localDateTime);
|
||||
//将localDateTime转换成时间戳
|
||||
System.out.println("localDateTime:" + java.sql.Timestamp.valueOf(localDateTime).getTime());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 计算周五的活动下次开启的时间
|
||||
*/
|
||||
@Test
|
||||
public void test() {
|
||||
LocalDate date = LocalDate.of(2018, 12, 6);
|
||||
LocalTime time = LocalTime.of(0, 0, 0);
|
||||
|
||||
int days = DayOfWeek.FRIDAY.getValue() - date.getDayOfWeek().getValue();
|
||||
LocalDate localDate = (days <= 0) ? date.plusDays(DayOfWeek.SUNDAY.getValue() + days) : date.plusDays(days);
|
||||
System.out.println(localDate);
|
||||
|
||||
LocalDateTime localDateTime = LocalDateTime.of(date, time);
|
||||
System.out.println(localDateTime);
|
||||
System.out.println("date:" + new Date(java.sql.Timestamp.valueOf(localDateTime).getTime()));
|
||||
}
|
||||
|
||||
@Ignore
|
||||
@Test
|
||||
public void nowTest() {
|
||||
for (int i = 0; i < 100; i++) {
|
||||
System.out.println(TimeUtils.now());
|
||||
ThreadUtils.sleep(1000);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:context="http://www.springframework.org/schema/context"
|
||||
|
||||
xmlns:scheduler="http://www.zfoo.com/schema/scheduler"
|
||||
|
||||
xsi:schemaLocation="
|
||||
http://www.springframework.org/schema/beans
|
||||
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
|
||||
http://www.springframework.org/schema/context
|
||||
http://www.springframework.org/schema/context/spring-context-4.0.xsd
|
||||
|
||||
http://www.zfoo.com/schema/scheduler
|
||||
http://www.zfoo.com/schema/scheduler-1.0.xsd">
|
||||
|
||||
|
||||
<context:component-scan base-package="com.zfoo.scheduler"/>
|
||||
|
||||
<scheduler:scheduler id="schedulerManager"/>
|
||||
|
||||
</beans>
|
||||
@@ -0,0 +1,44 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<configuration scan="false" debug="false">
|
||||
|
||||
<contextName>com.zfoo.scheduler</contextName>
|
||||
|
||||
<property name="LOG_HOME" value="log/scheduler"/>
|
||||
<property name="PATTERN_FILE"
|
||||
value="%d{yyyy-MM-dd HH:mm:ss} [%5level] [%thread] %logger.%M\\(%F:%line\\) - %msg%n"/>
|
||||
<property name="PATTERN_CONSOLE"
|
||||
value="%d{yyyy-MM-dd HH:mm:ss} [%highlight(%5level)] [%thread] %logger.%M\\(%F:%line\\) - %msg%n"/>
|
||||
<!-- 负责写日志,控制台日志,会打印所有的包的所有级别日志 -->
|
||||
<appender name="zfoo_console" class="ch.qos.logback.core.ConsoleAppender">
|
||||
<encoder>
|
||||
<pattern>${PATTERN_CONSOLE}</pattern>
|
||||
<charset>UTF-8</charset>
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
<!-- 根logger -->
|
||||
<root level="info">
|
||||
<appender-ref ref="zfoo_console"/>
|
||||
</root>
|
||||
|
||||
<!--
|
||||
- 1.name:包名或类名,用来指定受此logger约束的某一个包或者具体的某一个类
|
||||
- 2.未设置打印级别,所以继承他的上级<root>的日志级别“DEBUG”
|
||||
- 3.未设置additivity,默认为true,将此logger的打印信息向上级传递;
|
||||
- 4.未设置appender,此logger本身不打印任何信息,级别为“DEBUG”及大于“DEBUG”的日志信息传递给root,
|
||||
- root接到下级传递的信息,交给已经配置好的名为“STDOUT”的appender处理,“STDOUT”appender将信息打印到控制台;
|
||||
-->
|
||||
<logger name="ch.qos.logback" level="info"/>
|
||||
|
||||
<!--*******************************************Spring********************************************************-->
|
||||
<!--logger中的name是指代码的包名或类名,路径要写全,可以配置不同包中的日志输出到不同的文件中。level是日志输出级别 -->
|
||||
<!--过滤掉spring的一些无用的DEBUG信息-->
|
||||
<logger name="org.springframework" level="info"/>
|
||||
<!-- additivity="false"表示不继承父logger的配置和父类没有关系-->
|
||||
<logger name="org.springframework.core" level="warn"/>
|
||||
|
||||
|
||||
<!--*******************************************Netty*********************************************************-->
|
||||
<logger name="io.netty" level="info"/>
|
||||
</configuration>
|
||||
Reference in new issue
Block a user