初始任务执行和调度
我们需要定时计算各帖子的分数,清除临时文件等,这就需要任务调度的组件。
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的配置参数是保存在数据库中。
1.配置文件
#TaskExecutionProperties
task:
execution:
pool:
core-size: 5
max-size: 15
queue-capacity: 100
#TaskSchedulingProperties
scheduling:
pool:
size: 5
2.配置类ThreadPoolConfig
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.annotation.EnableScheduling;
@Configuration
@EnableScheduling
@EnableAsync
public class ThreadPoolConfig {
}
3.测试
3.1 AlphaService
//让该方法在多线程环境下,被异步的调用
@Async
public void execute1(){
logger.debug("execute1");
}
@Scheduled(initialDelay = 10000,fixedRate = 1000)
public void execute2(){
logger.debug("execute2");
}
3.2 ThreadPoolTests
import com.nowcoder.community.service.AlphaService;
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.*;
@RunWith(SpringRunner.class)
@SpringBootTest
@ContextConfiguration(classes = CommunityApplication.class)
public class ThreadPoolTests {
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;
// 4.Spring可执行定时任务的线程池
@Autowired
private ThreadPoolTaskScheduler taskScheduler;
@Autowired
private AlphaService alphaService;
private void sleep(long m){
try {
Thread.sleep(m);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 1.JDK普通线程池
@Test
public void testExecutorService(){
for (int i = 0; i < 10; i++) {
executorService.execute(()->{logger.debug("Hello ExecutorService");});
}
sleep(10000);
}
// 2.JDK定时任务线程池
@Test
public void testScheduledExecutorService(){
scheduledExecutorService.scheduleAtFixedRate(()->{logger.debug("Hello ScheduledExecutorService");},
10000,1000, TimeUnit.MILLISECONDS);
sleep(30000);
}
// 3.Spring普通线程池
@Test
public void testThreadPoolTaskExecutor(){
for (int i = 0; i < 10; i++) {
taskExecutor.execute(()->{logger.debug("Hello ThreadPoolTaskExecutor");});
}
sleep(10000);
}
// 4.Spring定时任务线程池
@Test
public void testThreadPoolTaskScheduler(){
Date startTime=new Date(System.currentTimeMillis()+10000);
taskScheduler.scheduleAtFixedRate(()->{logger.debug("Hello ThreadPoolTaskScheduler");},startTime,1000);
sleep(30000);
}
// 5.Spring普通线程池(简化)
@Test
public void testThreadPoolTaskExecutorSimple(){
for (int i = 0; i < 10; i++) {
alphaService.execute1();
}
sleep(10000);
}
// 6.Spring定时任务线程池(简化)
@Test
public void testThreadPoolTaskSchedulerSimple() {
sleep(30000);
}
}
4.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运行时的参数
4.1 导入依赖
<!--quartz-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-quartz</artifactId>
</dependency>
4.2 实现Job接口 AlphaJob
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");
}
}
4.3 配置类QuartzConfig
import com.nowcoder.community.quartz.AlphaJob;
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;
@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是否长久的存在
factoryBean.setRequestsRecovery(true); //job是否可被恢复
return factoryBean;
}
//2.配置trigger
// SimpleTriggerFactoryBean:简单的定时
// CronTriggerFactoryBean:可实现复杂的定时,如每年的1号进行清除
@Bean
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.4 配置文件
# QuartzProperties
quartz:
job-store-type: jdbc #存储方式
scheduler-name: communityScheduler #调度器名称
properties: {
org.quartz.scheduler.instanceId: AUTO, #自动生成调度器的id
org.quartz.jobStore.class: org.quartz.impl.jdbcjobstore.JobStoreTX, #存储需要的类
org.quartz.jobStore.driverDelegateClass: org.quartz.impl.jdbcjobstore.StdJDBCDelegate, #驱动
org.quartz.jobStore.isClustered: true, #采用集群的方式
org.quartz.threadPool.class: org.quartz.simpl.SimpleThreadPool, #所用线程池
org.quartz.threadPool.threadCount: 5 #线程池的数量
}
4.5 删除Quartz数据库中job QuartzTests
import org.junit.Test;
import org.junit.runner.RunWith;
import org.quartz.JobKey;
import org.quartz.Scheduler;
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(){
// JobKey(job的名称,job的组名)
try {
boolean b=scheduler.deleteJob(new JobKey("alphaJob","alphaJobGroup"));
System.out.println(b);
} catch (Exception e) {
e.printStackTrace();
}
}
}