首頁>技術>

定時任務應用非常廣泛,Java提供的現有解決方案有很多。本次主要講schedule、quartz、xxl-job、shedlock等相關的程式碼實踐。

一、SpringBoot使用Schedule

核心程式碼:

@Componentpublic class ScheduleTask {    private Logger logger = LoggerFactory.getLogger(ScheduleTask.class);    @Scheduled(cron = "0/1 * * * * ? ")    public void one() {        logger.info("one:" + new Date());    }    @Scheduled(cron = "0/1 * * * * ? ")    public void two() {        logger.info("two:" + new Date());    }    @Scheduled(cron = "0/1 * * * * ? ")    public void three() {        logger.info("three:" + new Date());    }}

執行效果如下:

除此之外還可以這樣實現,核心程式碼:

@PropertySource(value = {        "classpath:task.properties",}, encoding = "utf-8")@Component("scheduleTask")public class ScheduleTask implements SchedulingConfigurer {    @Value("${TEST_JOB_TASK_CRON}")    private String cron;    @Override    public void configureTasks(ScheduledTaskRegistrar scheduledTaskRegistrar) {        scheduledTaskRegistrar.addTriggerTask(new Runnable() {            @Override            public void run() {                System.out.println("執行任務:" + DateUtil.date());            }        }, new Trigger() {            @Override            public Date nextExecutionTime(TriggerContext triggerContext) {                return new CronTrigger(cron).nextExecutionTime(triggerContext);            }        });    }    public void setCron(String cron) {        this.cron = cron;    }}

有朋友或許很疑惑,為什麼要寫這麼一大堆,這個與前面的程式碼又有何區別呢?

區別是多執行緒並行。其實多執行緒並行也可以不用這麼寫,只需寫一段核心配置類程式碼即可。

定時任務多執行緒配置類:

@Configurationpublic class ScheduleConfig implements SchedulingConfigurer {    public void configureTasks(ScheduledTaskRegistrar scheduledTaskRegistrar) {        scheduledTaskRegistrar.setScheduler(Executors.newScheduledThreadPool(5));    }}

再次啟動,檢視效果,如下:

由此看出走不同的執行緒執行,不同的執行緒執行的好處是,如果某一個執行緒掛掉後,並不會阻塞導致其它定時任務無法執行。

另外如果要想併發執行,前面的配置可以不要,直接用SpringBoot提供的現成註解即可,核心程式碼如下:

@Component@EnableAsyncpublic class ScheduleAsyncTask {    private Logger logger = LoggerFactory.getLogger(ScheduleAsyncTask.class);    @Scheduled(cron = "0/1 * * * * ? ")    @Async    public void one() {        logger.info("one Async:" + new Date());    }    @Scheduled(cron = "0/1 * * * * ? ")    @Async    public void two() {        logger.info("two Async:" + new Date());    }    @Scheduled(cron = "0/1 * * * * ? ")    @Async    public void three() {        logger.info("three Async:" + new Date());    }}

除此外,還有基於schedule動態定時任務(所謂動態只不過是指cron表示式放在對應的資料表裡),簡單示例程式碼:

@Configurationpublic class DynamicScheduleTask implements SchedulingConfigurer {    @Autowired    @SuppressWarnings("all")    CronMapper cronMapper;    @Mapper    public interface CronMapper {        @Select("select cron from cron limit 1")        public String getCron();    }    /**     * 執行定時任務.     */    public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {        taskRegistrar.addTriggerTask(                //1.新增任務內容(Runnable)                () -> System.out.println("執行動態定時任務: " + LocalDateTime.now().toLocalTime()),                //2.設定執行週期(Trigger)                triggerContext -> {                    //2.1 從資料庫獲取執行週期                    String cron = cronMapper.getCron();                    //2.2 合法性校驗.                    if (StringUtils.isEmpty(cron)) {                        // Omitted Code ..                    }                    //2.3 返回執行週期(Date)                    return new CronTrigger(cron).nextExecutionTime(triggerContext);                }        );    }}

核心配置檔案(application.yml):

spring:  datasource:    url: jdbc:mysql://127.0.0.1:3306/test    username: root    password: 1234

SQL指令碼:

DROP DATABASE IF EXISTS `test`;CREATE DATABASE `test`;USE `test`;DROP TABLE IF EXISTS `cron`;CREATE TABLE `cron`  (  `cron_id` varchar(30) NOT NULL PRIMARY KEY,  `cron` varchar(30) NOT NULL  );INSERT INTO `cron` VALUES ('1', '0/5 * * * * ?');

執行效果如下:

(2)控制檯不斷輸出定時任務執行日誌

三、SpringBoot使用xxl-job

之前寫過一樣的例子,如今簡化了下。關於xxl-job使用詳情,可以參考我的這篇文章:SpringBoot整合Xxl-Job

1.Maven依賴
<dependency>    <groupId>com.xuxueli</groupId>    <artifactId>xxl-job-core</artifactId>    <version>2.2.0</version></dependency>
2.配置類
@Configurationpublic class XxlJobConfig {    private Logger logger = LoggerFactory.getLogger(XxlJobConfig.class);    @Value("${xxl.job.admin.addresses}")    private String adminAddresses;    @Value("${xxl.job.executor.appname}")    private String appName;    @Value("${xxl.job.executor.ip}")    private String ip;    @Value("${xxl.job.executor.port}")    private int port;    @Value("${xxl.job.accessToken}")    private String accessToken;    @Value("${xxl.job.executor.logpath}")    private String logPath;    @Value("${xxl.job.executor.logretentiondays}")    private int logRetentionDays;    @Bean(initMethod = "start", destroyMethod = "destroy")    public XxlJobSpringExecutor xxlJobExecutor() {        logger.info(">>>>>>>>>>> xxl-job config init.");        XxlJobSpringExecutor xxlJobSpringExecutor = new XxlJobSpringExecutor();        xxlJobSpringExecutor.setAdminAddresses(adminAddresses);        xxlJobSpringExecutor.setAppname(appName);        xxlJobSpringExecutor.setIp(ip);        xxlJobSpringExecutor.setPort(port);        xxlJobSpringExecutor.setAccessToken(accessToken);        xxlJobSpringExecutor.setLogPath(logPath);        xxlJobSpringExecutor.setLogRetentionDays(logRetentionDays);        return xxlJobSpringExecutor;    }}
3.配置檔案內容
# web portserver.port=8081# no web#spring.main.web-environment=false### xxl-job admin address list, such as "http://address" or "http://address01,http://address02"xxl.job.admin.addresses=http://127.0.0.1:8080/xxl-job-admin### xxl-job, access tokenxxl.job.accessToken=### xxl-job executor appnamexxl.job.executor.appname=blog-job-xxl-job### xxl-job executor registry-address: default use address to registry , otherwise use ip:port if address is nullxxl.job.executor.address=### xxl-job executor server-infoxxl.job.executor.ip=xxl.job.executor.port=8888### xxl-job executor log-pathxxl.job.executor.logpath=/data/applogs/xxl-job/jobhandler### xxl-job executor log-retention-daysxxl.job.executor.logretentiondays=30
4.定時任務類
@Componentpublic class XxlJobTaskExample {    @XxlJob("blogJobHandler")    public ReturnT<String> blogJobHandler(String param) throws Exception {        System.out.println("執行");        XxlJobLogger.log("XXL-JOB, Hello World.");        for (int i = 0; i < 5; i++) {            XxlJobLogger.log("beat at:" + i);            TimeUnit.SECONDS.sleep(2);        }        return ReturnT.SUCCESS;    }}
5.執行效果

分別如下所示:

@Configuration@EnableSchedulerLock(defaultLockAtMostFor = "PT30M")public class ShedLockConfig {    @Bean    public LockProvider lockProvider(RedisTemplate redisTemplate) {        return new RedisLockProvider(redisTemplate.getConnectionFactory());    }}
3.編寫具體的定時任務
@Componentpublic class TaskSchedule {    /**     * 每分鐘執行一次     * [秒] [分] [小時] [日] [月] [周] [年]     */    @Scheduled(cron = "1 * * * * ?")    @SchedulerLock(name = "synchronousSchedule")    public void SynchronousSchedule() {        System.out.println("Start run schedule to synchronous data:" + new Date());    }}
4.編寫啟動類
@SpringBootApplication@EnableSchedulingpublic class ShedLockRedisApplication {    public static void main(String[] args) {        SpringApplication.run(ShedLockRedisApplication.class);    }}
5.配置檔案
server:  tomcat:    uri-encoding: UTF-8    max-threads: 1000    min-spare-threads: 30  port: 8083spring:  redis:    database: 0    host: localhost    port: 6379    password:      # 密碼(預設為空)    timeout: 6000ms  # 連線超時時長(毫秒)    jedis:      pool:        max-active: 1000  # 連線池最大連線數(使用負值表示沒有限制)        max-wait: -1ms      # 連線池最大阻塞等待時間(使用負值表示沒有限制)        max-idle: 10      # 連線池中的最大空閒連線        min-idle: 5       # 連線池中的最小空閒連線
6.測試

我之所以用shedlock是因為確保在叢集環境下各微服務的定時任務只執行一個,而不是全部都執行相同的定時任務。

本次測試效果如下:

本次程式碼例子已放至我的GitHub:

https://github.com/developers-youcong/blog-job
source: https://developers-youcong.github.io/2020/11/21/Java之定時任務全家桶/

12
最新評論
  • BSA-TRITC(10mg/ml) TRITC-BSA 牛血清白蛋白改性標記羅丹明
  • 「python課程,精心總結」break使用