初始任务执行和调度
我们需要定时计算各帖子的分数,清除临时文件等,这就需要任务调度的组件。
JDK线程池
- ExecutorService:普通的线程池
- ScheduledExecutorService:可以执行定时任务(分布式环境可能出问题)
Spring 线程池
- ThreadPoolTaskExecutor:普通的线程池
- ThreadPoolTaskScheduler:可以执行定时任务(分布式环境可能出问题)
分布式定时任务
- Spring Quartz(将数据存储到数据库,分布式时可以共享数据)
- 核心调度接口Scheduler
- 定义任务的接口Job的execute方法
- Jobdetail接口来配置Job的名字、组等
- Trigger接口配置Job的什么时候运行、运行频率
- QuartzConfig:配置 -> 数据库 -> 调用
- FactoryBean可简化Bean的实例化过程:
- 通过FactoryBean封装Bean的实例化过程
- 将FactoryBean装配到Spring容器里
- 将FactoryBean注入给其他的Bean.
- 该Bean得到的是FactoryBean所管理的对象实例.
如果我们采用JDK或者Spring的Scheduler执行定时任务在分布式环境可能出问题。因为JDK或者Spring的Scheduler设置的执行参数是保存在内存中,服务器之间不共享内存,当多个Scheduler操作时,就会产生问题。如果通过Quartz,就可以解决。Quartz的配置参数是保存在数据库中。
测试:
Spring需要进行配置:
#TaskExecutionProperties
#核心线程数量
spring.task.execution.pool.core-size=5
#最大线程数量
spring.task.execution.pool.max-size=15
#工作队列容量
spring.task.execution.pool.queue-capacity=100
#TaskSchedulingProperties
spring.task.scheduling.pool.size=5
配置类:
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.annotation.EnableScheduling;
@Configuration //表示配置类
@EnableScheduling //开始spring的定时线程池
@EnableAsync //开启注解spring的线程池 -->@Async @Scheduled(initialDelay = 10000, fixedRate = 1000)
public class ThreadPoolConfig {
}
import org.junit.Test;
import org.junit.runner.RunWith;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringRunner;
import java.util.Date;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
@RunWith(SpringRunner.class)
@SpringBootTest
@ContextConfiguration(classes = CommunityApplication.class)
public class ThreadPoolTests {
// logger
private static final Logger logger = LoggerFactory.getLogger(ThreadPoolTests.class);
// 1. JDK普通的线程池
private ExecutorService executorService = Executors.newFixedThreadPool(5);
// 2. JDK可执行定时任务的线程池
private ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(5);
// 3. spring的线程池
@Autowired
private ThreadPoolTaskExecutor taskExecutor;
@Autowired
private ThreadPoolTaskScheduler taskScheduler;
@Autowired
private AlphaService alphaService;
//封装sleep方法
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.debug("JDK普通的线程池");
}
};
for (int i = 0; i < 10; i++) {
executorService.submit(task);
}
sleep(10000);
}
@Test
public void testScheduledExecutorService(){
Runnable task = new Runnable() {
@Override
public void run() {
logger.debug("JDK可执行定时任务的线程池");
}
};
/**
* command:执行线程
* initialDelay:初始化延时
* period:两次开始执行最小间隔时间
* unit:计时单位
*/
scheduledExecutorService.scheduleAtFixedRate(task, 10000, 1000, TimeUnit.MILLISECONDS);
sleep(30000);
}
//spring普通线程池
@Test
public void testThreadPoolTaskExecutor(){
Runnable task = new Runnable() {
@Override
public void run() {
logger.debug("spring普通线程池");
}
};
for (int i = 0; i < 10; i++) {
taskExecutor.submit(task);
}
sleep(10000);
}
//spring定时线程池
@Test
public void testThreadPoolTaskScheduler(){
Runnable task = new Runnable() {
@Override
public void run() {
logger.debug("spring定时线程池");
}
};
//延迟一秒执行
Date startTime = new Date(System.currentTimeMillis() + 10000);
taskScheduler.scheduleAtFixedRate(task, startTime, 10000);
sleep(10000);
}
//简化版 spring普通
@Test
public void testEasyTask(){
for (int i = 0; i < 10; i++) {
alphaService.executel();
}
sleep(10000);
}
//简化版 spring定时线程池
@Test
public void testEasyTask2(){
sleep(10000);
}
/*
AlphaService中添加的方法
// 让该方法在多线程环境下,被异步的调用.
@Async
public void execute1() {
logger.debug("execute1");
}
@Scheduled(initialDelay = 10000, fixedRate = 1000)
public void execute2() {
logger.debug("execute2");
}
*/
}
Quartz:
因为Quartz是基于数据库的,首先初始化tables_mysql_innodb.sql
- qrtz_job_detail:对任务(job)描述的表
- qrtz_simple_triggers:触发器有关的简单配置
- qrtz_triggers:触发器有关的配置
- qrtz_scheduler_state:定时器的有关内容
- qrtz_locks:锁的信息
Quartz的基本组成部分:
- 调度器:Scheduler:核心调度接口
- 任务:时间Job接口,声明任务。通过JobDetail配置Job的详细参数
- 触发器:Trigger,包括SimpleTrigger和CronTrigger,配置Job运行时的参数
测试:
1.导包
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-quartz</artifactId>
</dependency>
2.实现job接口
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
//例子
public class AlphaJob implements Job {
@Override
public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
System.out.println(Thread.currentThread().getName()+"quartzJob");
}
}
3.编写配置类
import org.quartz.JobDataMap;
import org.quartz.JobDetail;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.quartz.JobDetailFactoryBean;
import org.springframework.scheduling.quartz.SimpleTriggerFactoryBean;
//QuartzConfig为Quartz的配置类
//作用:当在配置文件中配置Quartz信息后,在第一次调用时,将配置信息写回数据库中,之后就直接读取数据库中的信息
@Configuration
public class QuartzConfig {
//FactoryBean作用:简化bean的实例化过程
//1.通过FactoryBean封装Bean的实例化过程
//2.将FactoryBean装配到spring容器中
//3.将FactoryBean注入给其他的bean
//4.其他的bean即可获得FactoryBean所管理的对象实例
// 1. 配置jobDetail
@Bean
public JobDetailFactoryBean alphaJobDetail(){
JobDetailFactoryBean factoryBean = new JobDetailFactoryBean();
factoryBean.setJobClass(AlphaJob.class); //声明要配置的job类
factoryBean.setName("alphaJob"); //声明job的名称
factoryBean.setGroup("alphaJobGroup"); //声明job的分组
factoryBean.setDurability(true); //job是否长久的存在,true为长久存在
factoryBean.setRequestsRecovery(true); //job是否可被恢复
return factoryBean;
}
// 2. 配置trigger
// SimpleTriggerFactoryBean:简单的定时;
// CronTriggerFactoryBean:可实现复杂的定时,如每月的1号进行清除
@Bean //alphaTrigger即得到了JobDetail
public SimpleTriggerFactoryBean alphaTrigger(JobDetail alphaJobDetail){
SimpleTriggerFactoryBean factoryBean = new SimpleTriggerFactoryBean();
factoryBean.setJobDetail(alphaJobDetail); //确定JobDetail
factoryBean.setName("alphaTrigger"); //Trigger的名称
factoryBean.setGroup("alphaTriggerGroup"); //Trigger的分组
factoryBean.setRepeatInterval(3000); //时间间隔3秒
factoryBean.setJobDataMap(new JobDataMap()); //保存数据的类型
return factoryBean;
}
}
4.对Quartz进行配置
注:如果我们没有对Quartz进行配置,就会利用Quartz的默认配置进行运行,并不会将配置信息写回数据库中。
# QuartzProperties
spring.quartz.job-store-type=jdbc //存储方式
spring.quartz.scheduler-name=communityScheduler //调度器的名称
spring.quartz.properties.org.quartz.scheduler.instanceId=AUTO //自动生成调度器的id
spring.quartz.properties.org.quartz.jobStore.class=org.quartz.impl.jdbcjobstore.JobStoreTX //存储需要的类
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 //线程池的数量
//删除Quartz数据库中的job
import org.junit.Test;
import org.junit.runner.RunWith;
import org.quartz.JobKey;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringRunner;
@RunWith(SpringRunner.class)
@SpringBootTest
@ContextConfiguration(classes = CommunityApplication.class)
public class QuartzTest {
//删除Quartz数据库中的job
@Autowired
private Scheduler scheduler;
@Test
public void deleteJob(){
try {
// JobKey(job的名称, job的组名)
boolean b = scheduler.deleteJob(new JobKey("alphaJob", "alphaJobGroup"));
System.out.println(b);
} catch (SchedulerException e) {
e.printStackTrace();
}
}
}