在服务器中可能会有定时任务,但是不知道分布式系统下次会访问哪一个服务器,所以服务器中的任务就是相同的,这样会导致浪费。使用Quartz可以解决这个问题。
@RunWith(SpringRunner.class)
@SpringBootTest
@ContextConfiguration(classes = MyCommunityApplication.class)
public class ThreadPoolTest {
private Logger logger = LoggerFactory.getLogger(ThreadPoolTest.class);
// JDK's normal thread-pool
private ExecutorService executorService = Executors.newFixedThreadPool(5);
// JDK's thread pool that periodically executes tasks
private ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(5);
private void sleep(long m){
try{
Thread.sleep(m);
} catch(InterruptedException e){
e.printStackTrace();
}
}
@Test
public void testExecutorService() {
Runnable task = new Runnable() {
@Override
public void run() {
logger.info("hello executor service");
}
};
for(int i = 0; i < 10; i++) {
executorService.submit(task);
}
sleep(10000);
}
@Test
public void testScheduleExecutorService(){
Runnable task = new Runnable() {
@Override
public void run() {
logger.info("hello executor service");
}
};
// 初始时间间隔为10000ms,任务间隔为1000ms
for(int i = 0; i < 10; i++) {
scheduledExecutorService.scheduleAtFixedRate(task, 10000, 1000, TimeUnit.MILLISECONDS);
}
sleep(30000);
}
}
配置application.properties
# Spring thread pool
# TaskExecutionProperties
spring.task.execution.pool.core-size=5
spring.task.execution.pool.max-size=15
spring.task.execution.pool.queue-capacity=100
# TaskScheduleProperties
spring.task.scheduling.pool.size=5
Spring线程池默认不开启定时线程池,需要新建配置类手动开启:
@Configuration
@EnableScheduling // 允许定时线程池
@EnableAsync // 允许多线程执行
public class ThreadPoolConfig {
}
测试方法:
@RunWith(SpringRunner.class)
@SpringBootTest
@ContextConfiguration(classes = MyCommunityApplication.class)
public class ThreadPoolTest {
private Logger logger = LoggerFactory.getLogger(ThreadPoolTest.class);
// Spring's normal thread pool
@Autowired
private ThreadPoolTaskExecutor taskExecutor;
// Spring's thread pool that periodically executes tasks
// 如果配置类不加@EnableScheduling会报错
@Autowired
private ThreadPoolTaskScheduler taskScheduler;
private void sleep(long m){
try{
Thread.sleep(m);
} catch(InterruptedException e){
e.printStackTrace();
}
}
@Test
public void testThreadPoolTaskExecutor(){
Runnable task = new Runnable() {
@Override
public void run() {
logger.info("hello spring thread pool");
}
};
for(int i = 0; i < 10; i++){
taskExecutor.submit(task);
}
sleep(10000);
}
@Test
public void testThreadPoolTaskScheduler(){
Runnable task = new Runnable() {
@Override
public void run() {
logger.info("hello spring thread pool");
}
};
Date start = new Date(System.currentTimeMillis() + 10000);
// for(int i = 0; i < 10; i++){
taskScheduler.scheduleAtFixedRate(task, start, 1000);
// }
sleep(10000);
}
}
@Service
public class AlphaService {
// 此前已经在配置类上允许了异步及定时
// @EnableScheduling // 允许定时线程池
// @EnableAsync // 允许异步执行
// 加上这个注解说明是异步的
@Async
public void execute1(){
logger.info("hello");
}
// 加上这个注解说明是定时任务
// 第一次延迟10s,之后每次间隔1s
// @Scheduled默认为单线程,开启多个任务时,任务的执行时机会受上一个任务执行时间的影响。
@Scheduled(initialDelay = 10000, fixedRate = 1000)
public void execute2(){
logger.info("hello2");
}
}
@RunWith(SpringRunner.class)
@SpringBootTest
@ContextConfiguration(classes = MyCommunityApplication.class)
public class ThreadPoolTest {
private Logger logger = LoggerFactory.getLogger(ThreadPoolTest.class);
// Spring's normal thread pool
@Autowired
private ThreadPoolTaskExecutor taskExecutor;
// Spring's thread pool that periodically executes tasks
@Autowired
private ThreadPoolTaskScheduler taskScheduler;
@Autowired
private AlphaService alphaService;
@Test
public void testThreadPoolTaskExecutorSimple(){
for(int i=0; i< 10; ++ i){
alphaService.execute1();
}
sleep(10000);
}
@Test
public void testThreadPoolTaskExecutorSimple2(){
// 此处不需要调用alphaService.execute2方法
// 因为有Scheduled注解的方法在程序开始时会自动执行
// alphaService.execute2();
sleep(30000);
}
}
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-quartzartifactId>
dependency>
配置Quartz
# QuartzProperties
spring.quartz.job-store-type=jdbc
spring.quartz.scheduler-name=communityScheduler
spring.quartz.properties.org.quartz.scheduler.instanceId=AUTO
#spring.quartz.properties.org.quartz.jobStore.class=org.quartz.impl.jdbcjobstore.JobStoreTX
spring.quartz.properties.org.quartz.jobStore.class=org.springframework.scheduling.quartz.LocalDataSourceJobStore
spring.quartz.properties.org.quartz.jobStore.driverDelegateClass=org.quartz.impl.jdbcjobstore.StdJDBCDelegate
spring.quartz.properties.org.quartz.jobStore.isClustered=true
spring.quartz.properties.org.quartz.threadPool.class=org.quartz.simpl.SimpleThreadPool
spring.quartz.properties.org.quartz.threadPool.threadCount=5
定义Job类:
public class AlphaJob implements Job {
@Override
public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
System.out.println(Thread.currentThread().getName() + ":execute a quartz job");
}
}
定义Quartz的配置类,该配置类只执行一次便被存入数据库的几个表中
// this configuration just for the first execution, and then the configuration will be saved in the database
@Configuration
public class QuartzConfig {
// `FactoryBean` simplify the instantiation process of `Bean`
// 1.The instantiation process of `Bean` is encapsulated through `FactoryBean`
// 2.Assemble `FactoryBean` into `Spring`'s container
// 3.Inject `FactoryBean` into the other beans
// 4.This bean can acquire the object instance of the bean managed by the `FactoryBean`
// inject `JobDetailFactoryBean` into this class
// config `JobDetail`
@Bean
public JobDetailFactoryBean alphaJobDetail() {
JobDetailFactoryBean factoryBean = new JobDetailFactoryBean();
factoryBean.setJobClass(AlphaJob.class);
factoryBean.setName("alphaJob");
factoryBean.setGroup("alphaGroup");
// this task will be stored permanently although there are no triggers existing
factoryBean.setDurability(true);
// this task can be recovered after meeting some faults
factoryBean.setRequestsRecovery(true);
return factoryBean;
}
// the `JobDetail` used is the object instance in `JobDetailFactoryBean`
// config `Trigger(SimpleTriggerFactoryBean, CronTriggerFactoryBean)`
@Bean
public SimpleTriggerFactoryBean alphaTrigger(JobDetail alphaJobDetail) {
SimpleTriggerFactoryBean factoryBean = new SimpleTriggerFactoryBean();
factoryBean.setJobDetail(alphaJobDetail);
factoryBean.setName("alphaTrigger");
factoryBean.setGroup("alphaGroup");
// the interval of the trigger
factoryBean.setRepeatInterval(3000);
// use an object to store the status of the job, `JobDataMap()` is the default object
factoryBean.setJobDataMap(new JobDataMap());
return factoryBean;
}
}
启动程序后,Job自动执行,可以看到任务相关的信息已经自动加入到了表中:
@RunWith(SpringRunner.class)
@SpringBootTest
@ContextConfiguration(classes = MyCommunityApplication.class)
public class QuartzTest {
@Autowired
private Scheduler scheduler;
@Test
public void testDeleteJob(){
try {
boolean result = scheduler.deleteJobs(Collections.singletonList(new JobKey("alphaJob", "alphaGroup")));
System.out.println(result);
} catch (SchedulerException e) {
throw new RuntimeException(e);
}
}
}
可以看到,数据库中已经没有了关于任务的记录