定時任務在企業專案比較常用到,幾乎所有的專案都會牽扯該功能模組,定時任務一般會處理指定時間點執行某一些業務邏輯、間隔時間執行某一些業務邏輯等。我們在之前有講過SpringBoot
是已經整合了定時任務的,詳見:第二十六章:SpringBoot使用@Scheduled建立定時任務,那麼我們本章將會採用外接的quartz
定時任務框架來完成定時任務的分散式單節點持久化,我們為什麼要持久化定時任務呢?
在一些專案中定時任務可能是必不可少的,由於某種特殊的原因定時任務可能丟失,如重啟定時任務服務專案後,原記憶體中的定時任務就會被完全釋放!那對於我們來說可能是致命的問題。當然也有強制的辦法解決這類問題,但是如果我們把定時任務持久化到資料庫,像維護普通邏輯資料那樣維護任務,就會避免專案中遇到的種種的特殊情況。
本章目標
基於SpringBoot
架構整合定時任務框架quartz
來完成分散式單節點定時任務持久化,將任務持久化到資料庫,更好的預防任務丟失。
構建專案
我們使用idea
開發工具建立一個SpringBoot
專案,pom.xml依賴配置如下所示:
...省略部分配置
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<druid.version>1.1.5</druid.version>
<quartz.version>2.3.0</quartz.version>
</properties>
<dependencies>
<!--spring data jpa相關-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<!--web相關依賴-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--資料庫相關依賴-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>${druid.version}</version>
</dependency>
<!--quartz相關依賴-->
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId>
<version>${quartz.version}</version>
</dependency>
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz-jobs</artifactId>
<version>${quartz.version}</version>
</dependency>
<!--定時任務需要依賴context模組-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
...省略部分配置複製程式碼
我們採用的是quartz
官方最新版本2.3.0
,新版本的任務排程框架做出了很多封裝,使用也變得簡易明瞭。
建立初始化完成,下面我們來建立定時任務相關的Configuration
配置。
QuartzConfiguration
quartz
與Spring
相關框架的整合方式有很多種,我們今天採用jobDetail
使用Spring Ioc
託管方式來完成整合,我們可以在定時任務例項中使用Spring
注入註解完成業務邏輯處理,下面我先把全部的配置貼出來再逐步分析,配置類如下所示:
package com.hengyu.chapter39.configuration;
import org.quartz.spi.JobFactory;
import org.quartz.spi.TriggerFiredBundle;
import org.springframework.beans.factory.annotation.Autowire;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;
import org.springframework.scheduling.quartz.SpringBeanJobFactory;
import javax.sql.DataSource;
/**
* quartz定時任務配置
* ========================
* Created with IntelliJ IDEA.
* User:恆宇少年
* Date:2017/11/5
* Time:14:07
* 碼雲:http://git.oschina.net/jnyqy
* ========================
* @author 恆宇少年
*/
@Configuration
@EnableScheduling
public class QuartzConfiguration
{
/**
* 繼承org.springframework.scheduling.quartz.SpringBeanJobFactory
* 實現任務例項化方式
*/
public static class AutowiringSpringBeanJobFactory extends SpringBeanJobFactory implements
ApplicationContextAware {
private transient AutowireCapableBeanFactory beanFactory;
@Override
public void setApplicationContext(final ApplicationContext context) {
beanFactory = context.getAutowireCapableBeanFactory();
}
/**
* 將job例項交給spring ioc託管
* 我們在job例項實現類內可以直接使用spring注入的呼叫被spring ioc管理的例項
* @param bundle
* @return
* @throws Exception
*/
@Override
protected Object createJobInstance(final TriggerFiredBundle bundle) throws Exception {
final Object job = super.createJobInstance(bundle);
/**
* 將job例項交付給spring ioc
*/
beanFactory.autowireBean(job);
return job;
}
}
/**
* 配置任務工廠例項
* @param applicationContext spring上下文例項
* @return
*/
@Bean
public JobFactory jobFactory(ApplicationContext applicationContext)
{
/**
* 採用自定義任務工廠 整合spring例項來完成構建任務
* see {@link AutowiringSpringBeanJobFactory}
*/
AutowiringSpringBeanJobFactory jobFactory = new AutowiringSpringBeanJobFactory();
jobFactory.setApplicationContext(applicationContext);
return jobFactory;
}
/**
* 配置任務排程器
* 使用專案資料來源作為quartz資料來源
* @param jobFactory 自定義配置任務工廠
* @param dataSource 資料來源例項
* @return
* @throws Exception
*/
@Bean(destroyMethod = "destroy",autowire = Autowire.NO)
public SchedulerFactoryBean schedulerFactoryBean(JobFactory jobFactory, DataSource dataSource) throws Exception
{
SchedulerFactoryBean schedulerFactoryBean = new SchedulerFactoryBean();
//將spring管理job自定義工廠交由排程器維護
schedulerFactoryBean.setJobFactory(jobFactory);
//設定覆蓋已存在的任務
schedulerFactoryBean.setOverwriteExistingJobs(true);
//專案啟動完成後,等待2秒後開始執行排程器初始化
schedulerFactoryBean.setStartupDelay(2);
//設定排程器自動執行
schedulerFactoryBean.setAutoStartup(true);
//設定資料來源,使用與專案統一資料來源
schedulerFactoryBean.setDataSource(dataSource);
//設定上下文spring bean name
schedulerFactoryBean.setApplicationContextSchedulerContextKey("applicationContext");
//設定配置檔案位置
schedulerFactoryBean.setConfigLocation(new ClassPathResource("/quartz.properties"));
return schedulerFactoryBean;
}
}複製程式碼
AutowiringSpringBeanJobFactory
可以看到上面配置類中,AutowiringSpringBeanJobFactory
我們繼承了SpringBeanJobFactory
類,並且通過實現ApplicationContextAware
介面獲取ApplicationContext
設定方法,通過外部例項化時設定ApplicationContext
例項物件,在createJobInstance
方法內,我們採用AutowireCapableBeanFactory
來託管SpringBeanJobFactory
類中createJobInstance
方法返回的定時任務例項,這樣我們就可以在定時任務類內使用Spring Ioc
相關的註解進行注入業務邏輯例項了。
JobFactory
任務工廠是在本章配置排程器時所需要的例項,我們通過jobFactory
方法注入ApplicationContext
例項,來建立一個AutowiringSpringBeanJobFactory
物件,並且將物件例項託管到Spring Ioc
容器內。
SchedulerFactoryBean
我們本章採用的是專案內部資料來源的方式來設定排程器的jobSotre
,官方quartz
有兩種持久化的配置方案。
第一種:採用quartz.properties
配置檔案配置獨立的定時任務資料來源,可以與使用專案的資料庫完全獨立。
第二種:採用與建立專案統一個資料來源,定時任務持久化相關的表與業務邏輯在同一個資料庫內。
可以根據實際的專案需求採取不同的方案,我們本章主要是通過第二種方案來進行講解,在上面配置類中可以看到方法schedulerFactoryBean
內自動注入了JobFactory
例項,也就是我們自定義的AutowiringSpringBeanJobFactory
任務工廠例項,另外一個引數就是DataSource
,在我們引入spring-starter-data-jpa
依賴後會根據application.yml
檔案內的資料來源相關配置自動例項化DataSource
例項,這裡直接注入是沒有問題的。
我們通過呼叫SchedulerFactoryBean
物件的setConfigLocation
方法來設定quartz
定時任務框架的基本配置,配置檔案所在位置:resources/quartz.properties
=> classpath:/quartz.properties
下。
注意:quartz.properties配置檔案一定要放在
classpath
下,放在別的位置有部分功能不會生效。
下面我們來看下quartz.properties
檔案內的配置,如下所示:
#排程器例項名稱
org.quartz.scheduler.instanceName = quartzScheduler
#排程器例項編號自動生成
org.quartz.scheduler.instanceId = AUTO
#持久化方式配置
org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX
#持久化方式配置資料驅動,MySQL資料庫
org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.StdJDBCDelegate
#quartz相關資料表字首名
org.quartz.jobStore.tablePrefix = QRTZ_
#開啟分散式部署
org.quartz.jobStore.isClustered = true
#配置是否使用
org.quartz.jobStore.useProperties = false
#分散式節點有效性檢查時間間隔,單位:毫秒
org.quartz.jobStore.clusterCheckinInterval = 20000
#執行緒池實現類
org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool
#執行最大併發執行緒數量
org.quartz.threadPool.threadCount = 10
#執行緒優先順序
org.quartz.threadPool.threadPriority = 5
#配置為守護執行緒,設定後任務將不會執行
#org.quartz.threadPool.makeThreadsDaemons=true
#配置是否啟動自動載入資料庫內的定時任務,預設true
org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread = true複製程式碼
由於我們下一章需要做分散式多節點自動交付高可用,本章的配置檔案加入了分散式相關的配置。
在上面配置中org.quartz.jobStore.class
與org.quartz.jobStore.driverDelegateClass
是定時任務持久化的關鍵配置,配置了資料庫持久化定時任務以及採用MySQL
資料庫進行連線,當然這裡我們也可以配置其他的資料庫,如下所示:PostgreSQL
: org.quartz.impl.jdbcjobstore.PostgreSQLDelegate
Sybase
: org.quartz.impl.jdbcjobstore.SybaseDelegate
MSSQL
: org.quartz.impl.jdbcjobstore.MSSQLDelegate
HSQLDB
: org.quartz.impl.jdbcjobstore.HSQLDBDelegate
Oracle
: org.quartz.impl.jdbcjobstore.oracle.OracleDelegate
org.quartz.jobStore.tablePrefix
屬性配置了定時任務資料表的字首,在quartz
官方提供的建立表SQL指令碼
預設就是qrtz_
,在對應的XxxDelegate驅動類內
也是使用的預設值,所以這裡我們如果修改表名字首,配置可以去掉。
org.quartz.jobStore.isClustered
屬性配置了開啟定時任務分散式功能,再開啟分散式時對應屬性org.quartz.scheduler.instanceId
改成Auto
配置即可,例項唯一標識會自動生成,這個標識具體生成的內容,我們一會在執行的控制檯就可以看到了,定時任務分散式準備好後會輸出相關的分散式節點配置資訊。
建立表SQL會在本章原始碼resources
目錄下,原始碼地址gitee.com/hengboy/spr…。
準備測試
我們先來建立一個簡單的商品資料表,建表SQL
如下所示:
DROP TABLE IF EXISTS `basic_good_info`;
CREATE TABLE `basic_good_info` (
`BGI_ID` int(11) NOT NULL AUTO_INCREMENT COMMENT '商品編號',
`BGI_NAME` varchar(20) DEFAULT NULL COMMENT '商品名稱',
`BGI_PRICE` decimal(8,2) DEFAULT NULL COMMENT '單價',
`BGI_UNIT` varchar(10) DEFAULT NULL COMMENT '單位',
PRIMARY KEY (`BGI_ID`)
) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8 COMMENT='商品基本資訊';複製程式碼
GoodEntity
我們先來針對表basic_good_info
建立一個實體,並且新增JPA
相關的配置,如下所示:
package com.hengyu.chapter39.good.entity;
import lombok.Data;
import javax.persistence.*;
import java.math.BigDecimal;
/**
* ========================
*
* @author 恆宇少年
* Created with IntelliJ IDEA.
* Date:2017/11/5
* Time:14:59
* 碼雲:http://git.oschina.net/jnyqy
* ========================
*/
@Entity
@Table(name = "basic_good_info")
@Data
public class GoodInfoEntity
{
/**
* 商品編號
*/
@Id
@GeneratedValue
@Column(name = "bgi_id")
private Long id;
/**
* 商品名稱
*/
@Column(name = "bgi_name")
private String name;
/**
* 商品單位
*/
@Column(name = "bgi_unit")
private String unit;
/**
* 商品單價
*/
@Column(name = "bgi_price")
private BigDecimal price;
}複製程式碼
下面我們根據商品實體來建立JPA
介面,如下所示:
/**
* ========================
* Created with IntelliJ IDEA.
* Date:2017/11/5
* Time:14:55
* 碼雲:http://git.oschina.net/jnyqy
* ========================
* @author 恆宇少年
*/
public interface GoodInfoRepository
extends JpaRepository<GoodInfoEntity,Long>
{
}複製程式碼
接下來我們再來新增一個商品新增的控制器方法,如下所示:
/**
* ========================
*
* @author 恆宇少年
* Created with IntelliJ IDEA.
* Date:2017/11/5
* Time:15:02
* 碼雲:http://git.oschina.net/jnyqy
* ========================
*/
@RestController
@RequestMapping(value = "/good")
public class GoodController
{
/**
* 商品業務邏輯實現
*/
@Autowired
private GoodInfoService goodInfoService;
/**
* 新增商品
* @return
*/
@RequestMapping(value = "/save")
public Long save(GoodInfoEntity good) throws Exception
{
return goodInfoService.saveGood(good);
}
}複製程式碼
在請求商品新增方法時,我們呼叫了GoodInfoService
內的saveGood
方法,傳遞一個商品的例項作為引數。我們接下來看看該類內相關程式碼,如下所示:
/**
* 商品業務邏輯
* ========================
*
* @author 恆宇少年
* Created with IntelliJ IDEA.
* Date:2017/11/5
* Time:15:04
* 碼雲:http://git.oschina.net/jnyqy
* ========================
*/
@Service
@Transactional(rollbackFor = Exception.class)
public class GoodInfoService
{
/**
* 注入任務排程器
*/
@Autowired
private Scheduler scheduler;
/**
* 商品資料介面
*/
@Autowired
private GoodInfoRepository goodInfoRepository;
/**
* 儲存商品基本資訊
* @param good 商品例項
* @return
*/
public Long saveGood(GoodInfoEntity good) throws Exception
{
goodInfoRepository.save(good);
return good.getId();
}複製程式碼
我們只是作為儲存商品的操作,下面我們來模擬一個需求,在商品新增完成後1分鐘我們通知後續的邏輯進行下一步處理,同時開始商品庫存定時檢查的任務。
定義商品新增定時任務
我們先來建立一個任務例項,並且繼承org.springframework.scheduling.quartz.QuartzJobBean
抽象類,重寫父抽象類內的executeInternal
方法來實現任務的主體邏輯。如下所示:
/**
* 商品新增定時任務實現類
* ========================
* Created with IntelliJ IDEA.
* User:恆宇少年
* Date:2017/11/5
* Time:14:47
* 碼雲:http://git.oschina.net/jnyqy
* ========================
* @author 恆宇少年
*/
public class GoodAddTimer
extends QuartzJobBean
{
/**
* logback
*/
static Logger logger = LoggerFactory.getLogger(GoodAddTimer.class);
/**
* 定時任務邏輯實現方法
* 每當觸發器觸發時會執行該方法邏輯
* @param jobExecutionContext 任務執行上下文
* @throws JobExecutionException
*/
@Override
protected void executeInternal(JobExecutionContext jobExecutionContext) throws JobExecutionException {
logger.info("商品新增完成後執行任務,任務時間:{}",new Date());
}複製程式碼
在任務主體邏輯內,我們只是做了一個簡單的輸出任務執行的時間,下面我們再來建立庫存定時檢查任務。
定義商品庫存檢查任務
同樣需要繼承org.springframework.scheduling.quartz.QuartzJobBean
抽象類實現抽象類內的executeInternal
方法,如下所示:
/**
* 商品庫存檢查定時任務
* ========================
*
* @author 恆宇少年
* Created with IntelliJ IDEA.
* Date:2017/11/5
* Time:15:47
* 碼雲:http://git.oschina.net/jnyqy
* ========================
*/
public class GoodStockCheckTimer
extends QuartzJobBean
{
/**
* logback
*/
static Logger logger = LoggerFactory.getLogger(GoodStockCheckTimer.class);
@Override
protected void executeInternal(JobExecutionContext jobExecutionContext) throws JobExecutionException {
logger.info("執行庫存檢查定時任務,執行時間:{}",new Date());
}
}複製程式碼
都是簡單的做了下日誌的輸出,下面我們需要重構GoodInfoService
內的saveGood
方法,對應的新增上面兩個任務的建立。
設定商品新增任務到排程器
在GoodInfoService
類內新增buildCreateGoodTimer
方法用於例項化商品新增任務,如下所示:
/**
* 構建建立商品定時任務
*/
public void buildCreateGoodTimer() throws Exception
{
//設定開始時間為1分鐘後
long startAtTime = System.currentTimeMillis() + 1000 * 60;
//任務名稱
String name = UUID.randomUUID().toString();
//任務所屬分組
String group = GoodAddTimer.class.getName();
//建立任務
JobDetail jobDetail = JobBuilder.newJob(GoodAddTimer.class).withIdentity(name,group).build();
//建立任務觸發器
Trigger trigger = TriggerBuilder.newTrigger().withIdentity(name,group).startAt(new Date(startAtTime)).build();
//將觸發器與任務繫結到排程器內
scheduler.scheduleJob(jobDetail, trigger);
}複製程式碼
在上面方法中我們定義的GoodAddTimer
例項只執行一次,在商品新增完成後延遲1分鐘進行呼叫任務主體邏輯。
其中任務的名稱以及任務的分組是為了區分任務做的限制,在同一個分組下如果加入同樣名稱的任務,則會提示任務已經存在,新增失敗的提示。
我們通過JobDetail
來構建一個任務例項,設定GoodAddTimer
類作為任務執行目標物件,當任務被觸發時就會執行GoodAddTimer
內的executeInternal
方法。
一個任務需要設定對應的觸發器,觸發器也分為很多種,該任務中我們並沒有採用cron
表示式來設定觸發器,而是呼叫startAt
方法設定任務開始執行時間。
最後將任務以及任務的觸發器共同交付給任務排程器,這樣就完成了一個任務的設定。
設定商品庫存檢查到任務排程器
在GoodInfoService
類內新增buildGoodStockTimer
方法用於例項化商品新增任務,如下所示:
/**
* 構建商品庫存定時任務
* @throws Exception
*/
public void buildGoodStockTimer() throws Exception
{
//任務名稱
String name = UUID.randomUUID().toString();
//任務所屬分組
String group = GoodStockCheckTimer.class.getName();
CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule("0/30 * * * * ?");
//建立任務
JobDetail jobDetail = JobBuilder.newJob(GoodStockCheckTimer.class).withIdentity(name,group).build();
//建立任務觸發器
Trigger trigger = TriggerBuilder.newTrigger().withIdentity(name,group).withSchedule(scheduleBuilder).build();
//將觸發器與任務繫結到排程器內
scheduler.scheduleJob(jobDetail, trigger);
}複製程式碼
該任務的觸發器我們採用了cron
表示式來設定,每隔30秒執行一次任務主體邏輯。
任務觸發器在建立時
cron
表示式可以搭配startAt
方法來同時使用。
下面我們修改GoodInfoService
內的saveGood
方法,分別呼叫設定任務的兩個方法,如下所示:
/**
* 儲存商品基本資訊
* @param good 商品例項
* @return
*/
public Long saveGood(GoodInfoEntity good) throws Exception
{
goodInfoRepository.save(good);
//構建建立商品定時任務
buildCreateGoodTimer();
//構建商品庫存定時任務
buildGoodStockTimer();
return good.getId();
}複製程式碼
下面我們就來測試下任務是否可以順序的被持久化到資料庫,並且是否可以在重啟服務後執行重啟前新增的任務。
測試
下面我們來啟動專案,啟動成功後
,我們來檢視控制檯輸出的分散式節點的資訊,如下所示:
2017-11-05 18:09:40.052 INFO 7708 --- [ main] c.hengyu.chapter39.Chapter39Application : 【【【【【【定時任務分散式節點 - 1 已啟動】】】】】】
2017-11-05 18:09:42.005 INFO 7708 --- [lerFactoryBean]] o.s.s.quartz.SchedulerFactoryBean : Starting Quartz Scheduler now, after delay of 2 seconds
2017-11-05 18:09:42.027 INFO 7708 --- [lerFactoryBean]] o.s.s.quartz.LocalDataSourceJobStore : ClusterManager: detected 1 failed or restarted instances.
2017-11-05 18:09:42.027 INFO 7708 --- [lerFactoryBean]] o.s.s.quartz.LocalDataSourceJobStore : ClusterManager: Scanning for instance "yuqiyu1509876084785"'s failed in-progress jobs.
2017-11-05 18:09:42.031 INFO 7708 --- [lerFactoryBean]] o.s.s.quartz.LocalDataSourceJobStore : ClusterManager: ......Freed 1 acquired trigger(s).
2017-11-05 18:09:42.033 INFO 7708 --- [lerFactoryBean]] org.quartz.core.QuartzScheduler : Scheduler schedulerFactoryBean_$_yuqiyu1509876579404 started.複製程式碼
定時任務是在專案啟動後2秒進行執行初始化,並且通過ClusterManager
來完成了instance
的建立,建立的節點唯一標識為yuqiyu1509876084785
。
編寫商品控制器請求方法測試用例,如下所示:
@RunWith(SpringRunner.class)
@SpringBootTest
public class Chapter39ApplicationTests {
/**
* 模擬mvc測試物件
*/
private MockMvc mockMvc;
/**
* web專案上下文
*/
@Autowired
private WebApplicationContext webApplicationContext;
/**
* 所有測試方法執行之前執行該方法
*/
@Before
public void before() {
//獲取mockmvc物件例項
mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
}
/**
* 測試新增商品
* @throws Exception
*/
@Test
public void addGood() throws Exception
{
MvcResult result = mockMvc.perform(MockMvcRequestBuilders.post("/good/save")
.param("name","西瓜")
.param("unit","斤")
.param("price","12.88")
)
.andDo(MockMvcResultHandlers.print())
.andExpect(MockMvcResultMatchers.status().is(200))
.andReturn();
result.getResponse().setCharacterEncoding("UTF-8");
System.out.println(result.getResponse().getContentAsString());
}複製程式碼
測試用例相關文章請訪問第三十五章:SpringBoot與單元測試的小祕密,我們來執行addGood
測試方法,檢視控制檯輸出,如下所示:
....省略部分輸出
Hibernate: insert into basic_good_info (bgi_name, bgi_price, bgi_unit) values (?, ?, ?)
2017-11-05 18:06:35.699 TRACE 7560 --- [ main] o.h.type.descriptor.sql.BasicBinder : binding parameter [1] as [VARCHAR] - [西瓜]
2017-11-05 18:06:35.701 TRACE 7560 --- [ main] o.h.type.descriptor.sql.BasicBinder : binding parameter [2] as [NUMERIC] - [12.88]
2017-11-05 18:06:35.701 TRACE 7560 --- [ main] o.h.type.descriptor.sql.BasicBinder : binding parameter [3] as [VARCHAR] - [斤]
....省略部分輸出
8
....省略部分輸出複製程式碼
可以看到我們的商品已被成功的寫入到資料庫並且輸出的主鍵值,我們的任務是否也成功的被寫入到資料庫了呢?我們來檢視qrtz_job_details
表內任務列表,如下所示:
schedulerFactoryBean 7567c9d7-76f5-47f3-bc5d-b934f4c1063b com.hengyu.chapter39.timers.GoodStockCheckTimer com.hengyu.chapter39.timers.GoodStockCheckTimer 0 0 0 0 0xACED0005737200156F72672E71756172747A2E4A6F62446174614D61709FB083E8BFA9B0CB020000787200266F72672E71756172747A2E7574696C732E537472696E674B65794469727479466C61674D61708208E8C3FBC55D280200015A0013616C6C6F77735472616E7369656E74446174617872001D6F72672E71756172747A2E7574696C732E4469727479466C61674D617013E62EAD28760ACE0200025A000564697274794C00036D617074000F4C6A6176612F7574696C2F4D61703B787000737200116A6176612E7574696C2E486173684D61700507DAC1C31660D103000246000A6C6F6164466163746F724900097468726573686F6C6478703F40000000000010770800000010000000007800
schedulerFactoryBean e5e08ab0-9be3-43fb-93b8-b9490432a5d7 com.hengyu.chapter39.timers.GoodAddTimer com.hengyu.chapter39.timers.GoodAddTimer 0 0 0 0 0xACED0005737200156F72672E71756172747A2E4A6F62446174614D61709FB083E8BFA9B0CB020000787200266F72672E71756172747A2E7574696C732E537472696E674B65794469727479466C61674D61708208E8C3FBC55D280200015A0013616C6C6F77735472616E7369656E74446174617872001D6F72672E71756172747A2E7574696C732E4469727479466C61674D617013E62EAD28760ACE0200025A000564697274794C00036D617074000F4C6A6176612F7574696C2F4D61703B787000737200116A6176612E7574696C2E486173684D61700507DAC1C31660D103000246000A6C6F6164466163746F724900097468726573686F6C6478703F40000000000010770800000010000000007800複製程式碼
任務已經被成功的持久化到資料庫內,等待1分鐘後檢視控制檯輸出內容如下所示:
2017-11-05 18:12:30.017 INFO 7708 --- [ryBean_Worker-1] c.h.c.timers.GoodStockCheckTimer : 執行庫存檢查定時任務,執行時間:Sun Nov 05 18:12:30 CST 2017
2017-11-05 18:13:00.009 INFO 7708 --- [ryBean_Worker-2] c.h.c.timers.GoodStockCheckTimer : 執行庫存檢查定時任務,執行時間:Sun Nov 05 18:13:00 CST 2017
2017-11-05 18:13:02.090 INFO 7708 --- [ryBean_Worker-3] c.hengyu.chapter39.timers.GoodAddTimer : 商品新增完成後執行任務,任務時間:Sun Nov 05 18:13:02 CST 2017複製程式碼
根據輸出的內容來判定完全吻合我們的配置引數,庫存檢查為30秒執行一次,而新增成功後的提醒則是1分鐘後執行一次。執行完成後就會被直接銷燬,我們再來檢視資料庫表qrtz_job_details
,這時就可以看到還剩下1個任務
。
重啟服務任務是否自動執行?
下面我們把專案重啟下,然後觀察控制檯的輸出內容,如下所示:
2017-11-05 18:15:54.018 INFO 7536 --- [ main] c.hengyu.chapter39.Chapter39Application : 【【【【【【定時任務分散式節點 - 1 已啟動】】】】】】
2017-11-05 18:15:55.975 INFO 7536 --- [lerFactoryBean]] o.s.s.quartz.SchedulerFactoryBean : Starting Quartz Scheduler now, after delay of 2 seconds
2017-11-05 18:15:56.000 INFO 7536 --- [lerFactoryBean]] org.quartz.core.QuartzScheduler : Scheduler schedulerFactoryBean_$_yuqiyu1509876953202 started.
2017-11-05 18:16:15.999 INFO 7536 --- [_ClusterManager] o.s.s.quartz.LocalDataSourceJobStore : ClusterManager: detected 1 failed or restarted instances.
2017-11-05 18:16:16.000 INFO 7536 --- [_ClusterManager] o.s.s.quartz.LocalDataSourceJobStore : ClusterManager: Scanning for instance "yuqiyu1509876579404"'s failed in-progress jobs.
2017-11-05 18:16:16.005 INFO 7536 --- [_ClusterManager] o.s.s.quartz.LocalDataSourceJobStore : ClusterManager: ......Freed 1 acquired trigger(s).
2017-11-05 18:16:16.041 INFO 7536 --- [ryBean_Worker-1] c.h.c.timers.GoodStockCheckTimer : 執行庫存檢查定時任務,執行時間:Sun Nov 05 18:16:16 CST 2017複製程式碼
可以看到成功的自動執行了我們在重啟之前配置的任務。
總結
本章主要講解了SpringBoot
整合quartz
定時任務框架,完成了分散式單節點任務持久化,下一章我們會講解任務引數傳遞以及分散式多節點任務自動負載。
本章原始碼已經上傳到碼雲:
SpringBoot配套原始碼地址:gitee.com/hengboy/spr…
SpringCloud配套原始碼地址:gitee.com/hengboy/spr…
SpringBoot相關係列文章請訪問:目錄:SpringBoot學習目錄
QueryDSL相關係列文章請訪問:QueryDSL通用查詢框架學習目錄
SpringDataJPA相關係列文章請訪問:目錄:SpringDataJPA學習目錄
SpringBoot相關文章請訪問:目錄:SpringBoot學習目錄,感謝閱讀!
歡迎加入QQ技術交流群,共同進步。