自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

一文帶你吃透定時(shí)任務(wù)框架

開(kāi)發(fā) 前端
crontab 嚴(yán)格來(lái)說(shuō)并不是屬于 java 內(nèi)的,它是 linux 自帶的一個(gè)工具,可以周期性地執(zhí)行某個(gè)shell腳本或命令。

一、介紹

說(shuō)到定時(shí)任務(wù),相信大家都不陌生,在我們實(shí)際的工作中,用到定時(shí)任務(wù)的場(chǎng)景可以說(shuō)非常的多,例如:

  • 雙 11 的 0 點(diǎn),定時(shí)開(kāi)啟秒殺
  • 每月1號(hào),財(cái)務(wù)系統(tǒng)自動(dòng)拉取每個(gè)人的績(jī)效工資,用于薪資計(jì)算
  • 使用 TCP 長(zhǎng)連接時(shí),客戶(hù)端按照固定頻率定時(shí)向服務(wù)端發(fā)送心跳請(qǐng)求

等等,定時(shí)器像水和空氣一般,普遍存在于各個(gè)場(chǎng)景中,在實(shí)際的業(yè)務(wù)開(kāi)發(fā)中,基本上少不了定時(shí)任務(wù)的應(yīng)用。

總結(jié)起來(lái),一般定時(shí)任務(wù)的表現(xiàn)有以下幾個(gè)特征:

1.在某個(gè)時(shí)刻觸發(fā),例如11.11號(hào)0點(diǎn)開(kāi)啟秒殺

2.按照固定頻率周期性觸發(fā),例如每分鐘發(fā)送心跳請(qǐng)求

3.預(yù)約指定時(shí)刻開(kāi)始周期性觸發(fā),例如從12.1號(hào)開(kāi)始每天7點(diǎn)鬧鐘響起

說(shuō)了這么多,也不BB了,下面我們就來(lái)點(diǎn)干活,列舉一些實(shí)際項(xiàng)目中使用的相關(guān)工具!

二、crontab 定時(shí)器

2.1、介紹

crontab 嚴(yán)格來(lái)說(shuō)并不是屬于 java 內(nèi)的,它是 linux 自帶的一個(gè)工具,可以周期性地執(zhí)行某個(gè)shell腳本或命令。

由于 crontab 在實(shí)際開(kāi)發(fā)中應(yīng)用比較多, 特別是對(duì)于運(yùn)維的人,crontab 命令是必須用到的命令,自動(dòng)化運(yùn)維中一定少不了它,而且 crontab 表達(dá)式跟我們后面要介紹的其他定時(shí)任務(wù)框架,例如 Quartz,Spring Schedule 的 cron 表達(dá)式類(lèi)似,所以這里先介紹 crontab。

簡(jiǎn)而言之,crontab 就是一個(gè)自定義定時(shí)器。

命令格式如下:

.---------------- minute (0 - 59)
|  .------------- hour (0 - 23)
|  |  .---------- day of month (1 - 31)
|  |  |  .------- month (1 - 12) OR jan,feb,mar,apr ...
|  |  |  |  .---- day of week (0 - 6) (Sunday=0 or 7) ...
|  |  |  |  |
*  *  *  *  *  command
  • 第一個(gè)參數(shù)(minute):代表一小時(shí)內(nèi)的第幾分,范圍 0-59。
  • 第二個(gè)參數(shù)(hour):代表一天中的第幾小時(shí),范圍 0-23。
  • 第三個(gè)參數(shù)(day):代表一個(gè)月中的第幾天,范圍 1-31。
  • 第四個(gè)參數(shù)(month):代表一年中第幾個(gè)月,范圍 1-12。
  • 第五個(gè)參數(shù)(week):代表星期幾,范圍 0-7 (0及7都是星期天)。
  • 第六個(gè)參數(shù)(command):所要執(zhí)行的指令。

具體應(yīng)用的時(shí)候,時(shí)間定義大概是長(zhǎng)下面這個(gè)樣子!

# 每5分鐘執(zhí)行一次命令
*/5 * * * * Command
# 每小時(shí)的第5分鐘執(zhí)行一次命令
5 * * * * Command
# 指定每天下午的 6:30 執(zhí)行一次命令
30 18 * * * Command
# 指定每月8號(hào)的7:30分執(zhí)行一次命令
30 7 8 * * Command
# 指定每年的6月8日5:30執(zhí)行一次命令
30 5 8 6 * Command
# 指定每星期日的6:30執(zhí)行一次命令
30 6 * * 0 Command

2.2、具體示例

以centOS操作系統(tǒng)為例,創(chuàng)建一個(gè)定時(shí)任務(wù),每分鐘執(zhí)行某個(gè)指定shell腳本,過(guò)程如下!

  • 首先安裝 crond 相關(guān)服務(wù)
yum -y install cronie yum-cron
  • 編寫(xiě)一個(gè)輸出當(dāng)前時(shí)間到日志的shell腳本
#創(chuàng)建一個(gè)test.sh腳本
vim /root/shell/test.sh

#腳本內(nèi)容如下,將內(nèi)容輸出到file.log文件
echo `date '+%Y-%m-%d %H:%M:%S'` >> /root/shell/file.log
  • 先執(zhí)行一下腳本,觀察內(nèi)容是否輸出到file.log文件
sh /root/shell/test.sh
  • 查看日志文件內(nèi)容
cat /root/shell/file.log

如果出現(xiàn)以下內(nèi)容,說(shuō)明運(yùn)行正常!

圖片圖片

  • 接著再來(lái)創(chuàng)建一個(gè)定時(shí)任務(wù),每分鐘執(zhí)行一次test.sh
#編輯定時(shí)任務(wù)【刪除-添加-修改】
crontab -e
  • 在文件末尾加入如下信息
#每分鐘執(zhí)行一次test.sh腳本
*/1 * * * * sh /root/shell/test.sh
  • 如果想查定時(shí)任務(wù)是否加入,可以通過(guò)如下命令
#查看crontab定時(shí)任務(wù)
crontab -l

圖片圖片

  • 最后就是重啟定時(shí)任務(wù)
##重新載入配置
systemctl reload crond
#重啟服務(wù)
systemctl restart crond
  • 查看file.log文件實(shí)時(shí)輸出內(nèi)容,觀察test.sh腳本是否被執(zhí)行
tail -f /root/shell/file.log

從結(jié)果上看,運(yùn)行正常!

圖片圖片

  • 如果想查看定時(shí)任務(wù)日志,可通過(guò)如下命令進(jìn)行查看
tail -f /var/log/cron

如果想移除某個(gè)定時(shí)任務(wù),直接輸入crontab -e命令,并移除對(duì)應(yīng)的腳本,然后刷新配置、重啟服務(wù)即可!

如果想深入的了解crontab使用,可以參考這篇文章,里面總結(jié)得比較好, 這里就不再多說(shuō)了。

三、java 自帶的定時(shí)器

3.1、Timer

Timer 定時(shí)器,由 jdk 提供的java.util.Timer和java.util.TimerTask兩個(gè)類(lèi)組合實(shí)現(xiàn)。

其中TimerTask表示某個(gè)具體任務(wù),而Timer則是進(jìn)行調(diào)度任務(wù)處理。

實(shí)現(xiàn)過(guò)程也很簡(jiǎn)單,示例如下:

import java.util.Timer;
import java.util.TimerTask;

public class TimerTest extends TimerTask {

    private String jobName;

    public TimerTest(String jobName) {
        this.jobName = jobName;
    }

    @Override
    public void run() {
        System.out.println("execute " + jobName);
    }

    public static void main(String[] args) {
        Timer timer = new Timer();
        long delay1 = 1 * 1000;
        long period1 = 1 * 1000;
        // 從現(xiàn)在開(kāi)始 1 秒鐘之后,每隔 1 秒鐘執(zhí)行一次 job1
        timer.schedule(new TimerTest("job1"), delay1, period1);

        long delay2 = 2 * 1000;
        long period2 = 2 * 1000;
        // 從現(xiàn)在開(kāi)始 2 秒鐘之后,每隔 2 秒鐘執(zhí)行一次 job2
        timer.schedule(new TimerTest("job2"), delay2, period2);
    }
}

輸出結(jié)果:

execute job1
execute job2
execute job1
execute job1
execute job2
execute job1
execute job1
execute job2
...

Timer 的優(yōu)點(diǎn)在于簡(jiǎn)單易用,由于所有任務(wù)都是由同一個(gè)線程來(lái)調(diào)度,因此所有任務(wù)都是串行執(zhí)行的,同一時(shí)間只能有一個(gè)任務(wù)在執(zhí)行,前一個(gè)任務(wù)的延遲或異常都將會(huì)影響到之后的任務(wù)。

具體原因如下:

  • 1.當(dāng)一個(gè)線程拋出異常時(shí),整個(gè) Timer 都會(huì)停止運(yùn)行。例如上面的 job1 拋出異常的話,job2 也不會(huì)再跑了
  • 當(dāng)一個(gè)線程里面處理的時(shí)間非常長(zhǎng)的時(shí)候,會(huì)影響其他 job 的調(diào)度。例如,如果 job1 處理的時(shí)間要 1 分鐘, 那么 job2 至少要等 1 分鐘之后才能跑。

基于上面的原因,Timer 現(xiàn)在生產(chǎn)環(huán)境中都不在使用!

3.2、ScheduledExecutor

鑒于 Timer 的上述缺陷,從 Java 5 開(kāi)始,推出了基于線程池設(shè)計(jì)的 ScheduledExecutor。

其設(shè)計(jì)思想是,每一個(gè)被調(diào)度的任務(wù)都會(huì)由線程池中一個(gè)線程來(lái)管理執(zhí)行,因此任務(wù)是并發(fā)執(zhí)行的,相互之間不會(huì)受到干擾。需要注意的是,只有當(dāng)任務(wù)的執(zhí)行時(shí)間到來(lái)時(shí),ScheduedExecutor 才會(huì)真正啟動(dòng)一個(gè)線程,其余時(shí)間 ScheduledExecutor 都是在輪詢(xún)?nèi)蝿?wù)的狀態(tài)。

實(shí)現(xiàn)過(guò)程,示例如下:

import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

public class ScheduledExecutorTest implements Runnable {

    private String jobName;

    public ScheduledExecutorTest(String jobName) {
        this.jobName = jobName;
    }

    @Override
    public void run() {
        System.out.println("execute " + jobName);
    }

    public static void main(String[] args) {
        //設(shè)置10個(gè)核心線程
        ScheduledExecutorService service = Executors.newScheduledThreadPool(10);

        long initialDelay1 = 1;
        long period1 = 1;

        // 從現(xiàn)在開(kāi)始1秒鐘之后,每隔1秒鐘執(zhí)行一次job1
        service.scheduleAtFixedRate(
                new ScheduledExecutorTest("job1"), initialDelay1,
                period1, TimeUnit.SECONDS);

        long initialDelay2 = 2;
        long delay2 = 2;
        // 從現(xiàn)在開(kāi)始2秒鐘之后,每隔2秒鐘執(zhí)行一次job2
        service.scheduleWithFixedDelay(
                new ScheduledExecutorTest("job2"), initialDelay2,
                delay2, TimeUnit.SECONDS);
    }
}

輸出結(jié)果:

execute job1
execute job1
execute job2
execute job1
execute job1
execute job2
execute job1

在 ScheduledExecutorService 中,由initialDelay、delay和TimeUnit三個(gè)參數(shù)決定任務(wù)的執(zhí)行頻率。

其中:

  • TimeUnit:表示執(zhí)行的單位,例如:毫秒、秒、分、小時(shí)、天...
  • initialDelay:表示從現(xiàn)在開(kāi)始多少TimeUnit后執(zhí)行任務(wù)
  • delay:表示任務(wù)執(zhí)行周期,每隔多少TimeUnit執(zhí)行一次任務(wù)

從 api 上可以看到,ScheduledExecutorService 的出現(xiàn),完全可以替代 Timer  ,同時(shí)完美的解決上面所說(shuō)的 Timer 存在的兩個(gè)問(wèn)題!

  • 當(dāng)任務(wù)拋異常時(shí),即使異常沒(méi)有被捕獲, 線程池也還會(huì)新建線程,所以定時(shí)任務(wù)不會(huì)停止
  • 由于 ScheduledExecutorService 是不同線程處理不同的任務(wù),因此不管一個(gè)線程的運(yùn)行時(shí)間有多長(zhǎng),都不會(huì)影響到另外一個(gè)線程的運(yùn)行

但是 ScheduledExecutorService 也不是萬(wàn)能的,比如我想每月1號(hào)統(tǒng)計(jì)一次報(bào)表、每季度月末統(tǒng)計(jì)銷(xiāo)售額等等這樣的需求。

你會(huì)發(fā)現(xiàn)使用 ScheduledExecutorService 實(shí)現(xiàn)的時(shí)候,每次任務(wù)執(zhí)行之后,你需要從當(dāng)前時(shí)間開(kāi)始出下一次執(zhí)行時(shí)間的間隔,而且每次都要重算,非常麻煩!

遇到這樣的需求,就需要一個(gè)更加完善的任務(wù)調(diào)度框架來(lái)解決這些復(fù)雜的調(diào)度問(wèn)題。

而我們所熟悉的開(kāi)源框架 Quartz 在這方面就提供了強(qiáng)大的支持。

四、第三方定時(shí)器

4.1、Quartz

quartz 在 java 項(xiàng)目中應(yīng)用非常的廣,市面上很多的開(kāi)源調(diào)度框架也基本都是直接或間接基于這個(gè)框架來(lái)開(kāi)發(fā)的。

下面我們就通過(guò)一個(gè)例子,來(lái)簡(jiǎn)單地認(rèn)識(shí)一下它。

  • 引入quartz包
<dependency>
    <groupId>org.quartz-scheduler</groupId>
    <artifactId>quartz</artifactId>
    <version>2.3.2</version>
</dependency>
  • 編寫(xiě)示例代碼
public class QuartzTest implements Job {

    @Override
    public void execute(JobExecutionContext context) throws JobExecutionException {
        System.out.println(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
    }

    public static void main(String[] args) throws SchedulerException {
        // 創(chuàng)建一個(gè)Scheduler
        Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();

        // 啟動(dòng)Scheduler
        scheduler.start();

        // 新建一個(gè)Job, 指定執(zhí)行類(lèi)是QuartzTest, 指定一個(gè)K/V類(lèi)型的數(shù)據(jù), 指定job的name和group
        JobDetail job = JobBuilder.newJob(QuartzTest.class)
                .usingJobData("jobData", "test")
                .withIdentity("myJob", "myJobGroup")
                .build();

        // 新建一個(gè)Trigger, 表示JobDetail的調(diào)度計(jì)劃, 這里的cron表達(dá)式是 每1秒執(zhí)行一次
        Trigger trigger = TriggerBuilder.newTrigger()
                .withIdentity("myTrigger", "myTriggerGroup")
                .startNow()
                .withSchedule(CronScheduleBuilder.cronSchedule("0/5 * * * * ?"))
                .build();

        // 讓scheduler開(kāi)始調(diào)度這個(gè)job, 按trigger指定的計(jì)劃
        scheduler.scheduleJob(job, trigger);
    }
}

輸出結(jié)果:

2020-11-09 21:38:40
2020-11-09 21:38:45
2020-11-09 21:38:50
2020-11-09 21:38:55
2020-11-09 21:39:00
2020-11-09 21:39:05
2020-11-09 21:39:10
...

當(dāng)然,你還可以從JobExecutionContext對(duì)象中,獲取上文usingJobData()方法中設(shè)置的值。

@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
    //從context中獲取instName,groupName以及dataMap
    String jobName = context.getJobDetail().getKey().getName();
    String groupName = context.getJobDetail().getKey().getGroup();
    JobDataMap dataMap = context.getJobDetail().getJobDataMap();
    //從dataMap中獲取myDescription,myValue以及myArray
    String value = dataMap.getString("jobData");
    System.out.println("jobName:" + jobName + ",groupName:" + groupName + ",jobData:" + value);
}

輸出結(jié)果:

jobName:myJob,groupName:myJobGroup,jobData:test

在 Quartz 工具包中,設(shè)計(jì)的核心類(lèi)主要包括 Scheduler, Job 以及 Trigger。

  • Scheduler:可以理解為是一個(gè)具體的調(diào)度實(shí)例,用來(lái)調(diào)度任務(wù)
  • JobDetail:定義具體作業(yè)的實(shí)例,進(jìn)一步封裝和拓展了 Job 功能,其中 Job 是一個(gè)接口,類(lèi)似上面的 TimerTask
  • Trigger:設(shè)置任務(wù)調(diào)度策略。例如多久執(zhí)行一次,什么時(shí)候執(zhí)行,以什么頻率執(zhí)行等等

相比 JDK 提供的任務(wù)調(diào)度服務(wù),Quartz 最明顯的一個(gè)特點(diǎn)就是將任務(wù)調(diào)度者、任務(wù)具體實(shí)例、任務(wù)調(diào)度策略進(jìn)行三方解耦,這么做的優(yōu)點(diǎn)在于同一個(gè) Job 可以綁定多個(gè)不同的 Trigger,同一個(gè) Trigger 也可以調(diào)度多個(gè) Job,配置靈活性非常強(qiáng)。

Trigger 同時(shí)還支持cron表達(dá)式,在任務(wù)調(diào)度時(shí)間配置方面,更加靈活。

當(dāng)然,Quartz 的用途不僅僅在單例服務(wù)上,在分布式調(diào)度方面也同樣應(yīng)用非常廣,由于篇幅原因,關(guān)于 Quartz 的詳細(xì)使用介紹,我們會(huì)在后期的文章中詳細(xì)深入分析。

4.2、Spring Schedule

與 Quartz 齊名的還有我們所熟悉的 Spring Schedule,由 Spring 原生提供支持。

實(shí)現(xiàn)上,Spring 中使用定時(shí)任務(wù)也非常簡(jiǎn)單。

4.2.1、基于 XML 配置
  • 在springApplication.xml配置文件中加入如下信息
<beans xmlns="http://www.springframework.org/schema/beans"  
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"   
    xmlns:p="http://www.springframework.org/schema/p"  
    xmlns:task="http://www.springframework.org/schema/task"  
    xmlns:context="http://www.springframework.org/schema/context"  
    xmlns:aop="http://www.springframework.org/schema/aop"   
    xsi:schemaLocation="http://www.springframework.org/schema/beans   
    http://www.springframework.org/schema/beans/spring-beans-4.0.xsd  
    http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd    
    http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-4.0.xsd    
    http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd    
    http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd    
    http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-4.0.xsd">  

    <!-- 定時(shí)器開(kāi)關(guān)-->  
    <task:annotation-driven />

    <!-- 將TestTask注入ioc-->
    <bean id="myTask" class="com.spring.task.TestTask"></bean>

    <!-- 定時(shí)調(diào)度方法配置-->
    <task:scheduled-tasks>
        <!-- 這里表示的是每隔5秒執(zhí)行一次 -->
        <task:scheduled ref="myTask" method="show" cron="*/5 * * * * ?" />
        <!-- 這里表示的是每隔10秒執(zhí)行一次 -->
        <task:scheduled ref="myTask" method="print" cron="*/10 * * * * ?"/>
    </task:scheduled-tasks>

    <!-- 自動(dòng)掃描的包名 -->
    <context:component-scan base-package="com.spring.task" />

</beans>
  • 定義自己的任務(wù)執(zhí)行邏輯
package com.spring.task;

/**
 * 定義任務(wù)
 */
public class TestTask {

    public void show() {
        System.out.println("show method 1");
    }

    public void print() {
        System.out.println("print method 1");
    }
}
4.2.2、基于注解配置

基于注解的配置,可以直接在方法上配置相應(yīng)的調(diào)度策略,相比xml的方式更加簡(jiǎn)潔。

  • 實(shí)現(xiàn)過(guò)程如下
package com.spring.task;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

/**
 * 基于注解的定時(shí)器
 */
@Component
public class TestTask2 {

    /**
     * 定時(shí)計(jì)算。每隔5秒執(zhí)行一次
     */
    @Scheduled(cron = "*/5 * * * * ?")
    public void show() {
        System.out.println("show method 2");
    }

    /**
     * 啟動(dòng)1分鐘后執(zhí)行,之后每隔10秒執(zhí)行一次
     */
    @Scheduled(initialDelay = 60*1000, fixedRate = 10*1000)
    public void print() {
        System.out.println("print method 2");
    }
}
4.2.3、Spring Boot 定時(shí)任務(wù)應(yīng)用

如果在 Spring Boot 項(xiàng)目中,使用就更加方便了。

  • 首先在程序入口啟動(dòng)類(lèi)添加@EnableScheduling,開(kāi)啟定時(shí)任務(wù)功能
@SpringBootApplication
@EnableScheduling
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}
  • 編寫(xiě)定時(shí)任務(wù)邏輯
@Component
public class TestTask3 {

    private int count=0;

    @Scheduled(crnotallow="*/5 * * * * ?")
    private void process() {
        System.out.println("this is scheduler task running  "+(count++));
    }
}

輸出結(jié)果:

this is scheduler task running  0
this is scheduler task running  1
this is scheduler task running  2
...
4.2.4、任務(wù)執(zhí)行規(guī)則

最后,我們來(lái)看看 Spring Schedule 的任務(wù)執(zhí)行規(guī)則,打開(kāi)@Scheduled注解類(lèi),源碼如下:

@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Repeatable(Schedules.class)
public @interface Scheduled {

    String cron() default "";

    String zone() default "";

    long fixedDelay() default -1;

    String fixedDelayString() default "";

    long fixedRate() default -1;

    String fixedRateString() default "";

    long initialDelay() default -1;

    String initialDelayString() default "";
}

從方法上可以看出,@Scheduled注解中可以傳 8 種參數(shù):

  • cron:指定 cron 表達(dá)式
  • zone:默認(rèn)使用服務(wù)器默認(rèn)時(shí)區(qū)??梢栽O(shè)置為java.util.TimeZone中的zoneId
  • fixedDelay:從上一個(gè)任務(wù)完成開(kāi)始到下一個(gè)任務(wù)開(kāi)始的間隔,單位毫秒
  • fixedDelayString:同上,時(shí)間值是String類(lèi)型
  • fixedRate:從上一個(gè)任務(wù)開(kāi)始到下一個(gè)任務(wù)開(kāi)始的間隔,單位毫秒
  • fixedRateString:同上,時(shí)間值是String類(lèi)型
  • initialDelay:任務(wù)首次執(zhí)行延遲的時(shí)間,單位毫秒
  • initialDelayString:同上,時(shí)間值是String類(lèi)型

其中用的最多的就是cron表達(dá)式,下面我們就一起來(lái)看看如何來(lái)編寫(xiě)配置。

4.2.5、Cron 表達(dá)式

Spring 的 cron 表達(dá)式和 Quartz 的 cron 表達(dá)式基本都是通用的,但是與 linux 下 crontab 的 cron 表達(dá)式是有一定區(qū)別的,它可以直接到秒級(jí)別。

cron 表達(dá)式結(jié)構(gòu):

.---------------------- seconds(0 - 59)
|  .------------------- minute (0 - 59)
|  |  .---------------- hour (0 - 23)
|  |  |  .------------- day of month (1 - 31)
|  |  |  |  .---------- month (1 - 12)
|  |  |  |  |  .------- Day-of-Week (1 - 7) 
|  |  |  |  |  |  .---- year (1970 - 2099) ...
|  |  |  |  |  |  |
*  *  *  *  *  ?  *

具體樣例如下:

圖片圖片

在 cron 表達(dá)式中不區(qū)分大小寫(xiě),更多配置可以參考這里。

還可以在線解析cron表達(dá)式進(jìn)行測(cè)試。

圖片圖片

4.3、elastic-job

elastic-job 是當(dāng)當(dāng)基于 quartz 二次開(kāi)發(fā)而開(kāi)源的一個(gè)分布式彈性作業(yè)框架,功能十分強(qiáng)大。

主要功能:

  • 分布式:重寫(xiě) Quartz 基于數(shù)據(jù)庫(kù)的分布式功能,改用 Zookeeper 實(shí)現(xiàn)注冊(cè)中心。
  • 并行調(diào)度:采用任務(wù)分片方式實(shí)現(xiàn)。將一個(gè)任務(wù)拆分為n個(gè)獨(dú)立的任務(wù)項(xiàng),由分布式的服務(wù)器并行執(zhí)行各自分配到的分片項(xiàng)。
  • 彈性擴(kuò)容縮容:將任務(wù)拆分為 n 個(gè)任務(wù)項(xiàng)后,各個(gè)服務(wù)器分別執(zhí)行各自分配到的任務(wù)項(xiàng)。一旦有新的服務(wù)器加入集群,或現(xiàn)有服務(wù)器下線,elastic-job將在保留本次任務(wù)執(zhí)行不變的情況下,下次任務(wù)開(kāi)始前觸發(fā)任務(wù)重分片。
  • 集中管理:采用基于Zookeeper的注冊(cè)中心,集中管理和協(xié)調(diào)分布式作業(yè)的狀態(tài),分配和監(jiān)聽(tīng)。外部系統(tǒng)可直接根據(jù)Zookeeper的數(shù)據(jù)管理和監(jiān)控elastic-job。
  • 定制化流程型任務(wù):作業(yè)可分為簡(jiǎn)單和數(shù)據(jù)流處理兩種模式,數(shù)據(jù)流又分為高吞吐處理模式和順序性處理模式,其中高吞吐處理模式可以開(kāi)啟足夠多的線程快速的處理數(shù)據(jù),而順序性處理模式將每個(gè)分片項(xiàng)分配到一個(gè)獨(dú)立線程,用于保證同一分片的順序性,這點(diǎn)類(lèi)似于kafka的分區(qū)順序性。

由于 elastic-job 是基于 Zookeeper 實(shí)現(xiàn)集群調(diào)度,因此在使用它之前,需要先搭建好 Zookeeper 服務(wù)器,網(wǎng)上教程很多,在此不過(guò)多介紹。

elastic-job 具體簡(jiǎn)單實(shí)現(xiàn)過(guò)程如下!

  • 引入相關(guān) elastic-job 依賴(lài)
<dependencies>
    <!-- 引入elastic-job-lite核心模塊 -->
    <dependency>
        <groupId>com.dangdang</groupId>
        <artifactId>elastic-job-lite-core</artifactId>
        <version>2.0.5</version>
    </dependency>
    <!-- 使用springframework自定義命名空間時(shí)引入 -->
    <dependency>
        <groupId>com.dangdang</groupId>
        <artifactId>elastic-job-lite-spring</artifactId>
        <version>2.0.5</version>
    </dependency>
    <dependency>
        <groupId>org.apache.zookeeper</groupId>
        <artifactId>zookeeper</artifactId>
        <version>3.4.9</version>
    </dependency>
</dependencies>
  • 定義job
public class MyElasticJob implements SimpleJob {

    public void execute(ShardingContext context) {
        System.out.println(context.toString());
        switch (context.getShardingItem()) {
            case 0:
                System.out.println("------------->>>>0");
                break;
            case 1:
                System.out.println("------------->>>>1");
                break;
            case 2:
                System.out.println("------------->>>>2");
                break;
            default:
                System.out.println("------------->>>>default");
                break;
        }
    }


    public static void main(String[] args) {
        new JobScheduler(createRegistryCenter(), createJobConfiguration()).init();
    }

    private static CoordinatorRegistryCenter createRegistryCenter() {
        //配置ZK注冊(cè)中心地址
        CoordinatorRegistryCenter regCenter = new ZookeeperRegistryCenter(new ZookeeperConfiguration("10.211.108.160:2181", "elastic-job-demo"));
        regCenter.init();
        return regCenter;
    }
    private static LiteJobConfiguration createJobConfiguration() {
        JobCoreConfiguration simpleCoreConfig = JobCoreConfiguration.newBuilder("demoSimpleJob", "0/15 * * * * ?", 10).build();
        // 定義SIMPLE類(lèi)型配置
        SimpleJobConfiguration simpleJobConfig = new SimpleJobConfiguration(simpleCoreConfig, MyElasticJob.class.getCanonicalName());
        // 定義Lite作業(yè)根配置
        LiteJobConfiguration simpleJobRootConfig = LiteJobConfiguration.newBuilder(simpleJobConfig).build();
        return simpleJobRootConfig;
    }
}

具體詳細(xì)使用,可以參考官方網(wǎng)站。

4.4、xxl-job

xxl-job 是被廣泛使用的另外一款使用的分布式任務(wù)調(diào)度框架,出自大眾點(diǎn)評(píng)許雪里(xxl 就是作者名字的拼音首字母)的開(kāi)源項(xiàng)目,官網(wǎng)上介紹這是一個(gè)輕量級(jí)分布式任務(wù)調(diào)度框架,其核心設(shè)計(jì)目標(biāo)是開(kāi)發(fā)迅速、學(xué)習(xí)簡(jiǎn)單、輕量級(jí)、易擴(kuò)展。

圖片圖片

跟elasticjob不同,xxl-job 環(huán)境依賴(lài)于mysql,不用 ZooKeeper,這也是最大的不同。早起的 xxljob 也是基于 quartz 開(kāi)發(fā)的,不過(guò)現(xiàn)在慢慢去 quartz 化了,改成自研的調(diào)度模塊。

xxl-job 具體簡(jiǎn)單實(shí)現(xiàn)過(guò)程如下!

  • 引入相關(guān) xxl-job 依賴(lài)
<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter</artifactId>
    </dependency>
    <dependency>
        <groupId>com.xuxueli</groupId>
        <artifactId>xxl-job-core</artifactId>
        <version>2.2.0</version>
    </dependency>
</dependencies>
  • 在application.properties添加相關(guān)配置
# web port
server.port=8081
# log config
logging.cnotallow=classpath:logback.xml
spring.application.name=xxljob-demo
### 調(diào)度中心部署跟地址 [選填]:如調(diào)度中心集群部署存在多個(gè)地址則用逗號(hào)分隔。執(zhí)行器將會(huì)使用該地址進(jìn)行"執(zhí)行器心跳注冊(cè)"和"任務(wù)結(jié)果回調(diào)";為空則關(guān)閉自動(dòng)注冊(cè);
xxl.job.admin.addresses=http://127.0.0.1:8080/xxl-job-admin
### 執(zhí)行器通訊TOKEN [選填]:非空時(shí)啟用;
xxl.job.accessToken=
### 執(zhí)行器AppName [選填]:執(zhí)行器心跳注冊(cè)分組依據(jù);為空則關(guān)閉自動(dòng)注冊(cè)
xxl.job.executor.appname=xxl-job-demo
### 執(zhí)行器注冊(cè) [選填]:優(yōu)先使用該配置作為注冊(cè)地址,為空時(shí)使用內(nèi)嵌服務(wù) ”IP:PORT“ 作為注冊(cè)地址。從而更靈活的支持容器類(lèi)型執(zhí)行器動(dòng)態(tài)IP和動(dòng)態(tài)映射端口問(wèn)題。
xxl.job.executor.address=
### 執(zhí)行器IP [選填]:默認(rèn)為空表示自動(dòng)獲取IP,多網(wǎng)卡時(shí)可手動(dòng)設(shè)置指定IP,該IP不會(huì)綁定Host僅作為通訊實(shí)用;地址信息用于 "執(zhí)行器注冊(cè)" 和 "調(diào)度中心請(qǐng)求并觸發(fā)任務(wù)";
xxl.job.executor.ip=
### 執(zhí)行器端口號(hào) [選填]:小于等于0則自動(dòng)獲??;默認(rèn)端口為9999,單機(jī)部署多個(gè)執(zhí)行器時(shí),注意要配置不同執(zhí)行器端口;
xxl.job.executor.port=9999
### 執(zhí)行器運(yùn)行日志文件存儲(chǔ)磁盤(pán)路徑 [選填] :需要對(duì)該路徑擁有讀寫(xiě)權(quán)限;為空則使用默認(rèn)路徑;
xxl.job.executor.logpath=/data/applogs/xxl-job/jobhandler
### 執(zhí)行器日志文件保存天數(shù) [選填] : 過(guò)期日志自動(dòng)清理, 限制值大于等于3時(shí)生效; 否則, 如-1, 關(guān)閉自動(dòng)清理功能;
xxl.job.executor.logretentinotallow=10
  • 編寫(xiě)一個(gè)配置類(lèi)XxlJobConfig
@Configuration
public class XxlJobConfig {
    private Logger logger = LoggerFactory.getLogger(XxlJobConfig.class);
    @Value("${xxl.job.admin.addresses}")
    private String adminAddresses;
    @Value("${xxl.job.accessToken}")
    private String accessToken;
    @Value("${xxl.job.executor.appname}")
    private String appname;
    @Value("${xxl.job.executor.address}")
    private String address;
    @Value("${xxl.job.executor.ip}")
    private String ip;
    @Value("${xxl.job.executor.port}")
    private int port;
    @Value("${xxl.job.executor.logpath}")
    private String logPath;
    @Value("${xxl.job.executor.logretentiondays}")
    private int logRetentionDays;

    @Bean
    public XxlJobSpringExecutor xxlJobExecutor() {
        logger.info(">>>>>>>>>>> xxl-job config init.");
        XxlJobSpringExecutor xxlJobSpringExecutor = new XxlJobSpringExecutor();
        xxlJobSpringExecutor.setAdminAddresses(adminAddresses);
        xxlJobSpringExecutor.setAppname(appname);
        xxlJobSpringExecutor.setAddress(address);
        xxlJobSpringExecutor.setIp(ip);
        xxlJobSpringExecutor.setPort(port);
        xxlJobSpringExecutor.setAccessToken(accessToken);
        xxlJobSpringExecutor.setLogPath(logPath);
        xxlJobSpringExecutor.setLogRetentionDays(logRetentionDays);
        return xxlJobSpringExecutor;
    }
}
  • 編寫(xiě)具體任務(wù)類(lèi)XxlJobDemoHandler
@Component
public class XxlJobDemoHandler {
    /**
     * Bean模式,一個(gè)方法為一個(gè)任務(wù)
     * 1、在Spring Bean實(shí)例中,開(kāi)發(fā)Job方法,方式格式要求為 "public ReturnT<String> execute(String param)"
     * 2、為Job方法添加注解 "@XxlJob(value="自定義jobhandler名稱(chēng)", init = "JobHandler初始化方法", destroy = "JobHandler銷(xiāo)毀方法")",注解value值對(duì)應(yīng)的是調(diào)度中心新建任務(wù)的JobHandler屬性的值。
     * 3、執(zhí)行日志:需要通過(guò) "XxlJobLogger.log" 打印執(zhí)行日志;
     */
    @XxlJob("demoJobHandler")
    public ReturnT<String> demoJobHandler(String param) throws Exception {
        XxlJobLogger.log("java, Hello World~~~");
        XxlJobLogger.log("param:" + param);
        return ReturnT.SUCCESS;
    }
}

寫(xiě)完之后啟動(dòng)服務(wù),然后可以打開(kāi)管理界面,找到執(zhí)行器管理,添加執(zhí)行器。

圖片圖片

接著到任務(wù)管理,添加任務(wù)。

圖片

最后我們可以到任務(wù)管理去測(cè)試一下,運(yùn)行demoJobHandler。

圖片圖片

圖片圖片

點(diǎn)擊保存后,會(huì)立即執(zhí)行。點(diǎn)擊查看日志,可以看到任務(wù)執(zhí)行的歷史日志記錄

圖片圖片

這就是簡(jiǎn)單的Demo演示,具體詳細(xì)使用,可以參考官方網(wǎng)站。

五、總結(jié)

本文主要圍繞定時(shí)調(diào)度器的理論知識(shí)和用法做了一次知識(shí)的總結(jié),如果有理解不對(duì)的地方,歡迎大家留言指出。

六、參考

1、https://juejin.im/post/6844904002606350343

2、https://developer.ibm.com/zh/languages/java/articles/j-lo-taskschedule/

3、https://www.cnblogs.com/linjiqin/p/11720673.html

4、https://www.cnkirito.moe/timer/

5、https://cloud.tencent.com/developer/article/1138669

6、https://developer.aliyun.com/article/775305

責(zé)任編輯:武曉燕 來(lái)源: Java極客技術(shù)
相關(guān)推薦

2024-03-12 11:39:30

Python開(kāi)發(fā)

2019-12-25 15:10:00

MySQL事件數(shù)據(jù)庫(kù)

2020-02-07 11:07:53

數(shù)組鏈表單鏈表

2022-12-20 07:39:46

2023-11-20 08:18:49

Netty服務(wù)器

2023-12-21 17:11:21

Containerd管理工具命令行

2023-11-06 08:16:19

APM系統(tǒng)運(yùn)維

2021-05-29 10:11:00

Kafa數(shù)據(jù)業(yè)務(wù)

2023-07-31 08:18:50

Docker參數(shù)容器

2022-11-11 19:09:13

架構(gòu)

2023-01-16 08:49:20

RocketMQ定時(shí)任務(wù)源代

2021-04-27 11:28:21

React.t事件元素

2024-02-19 00:00:00

分布式定時(shí)任務(wù)框架

2022-08-09 08:40:37

框架分布式定時(shí)任務(wù)

2022-02-24 07:34:10

SSL協(xié)議加密

2023-11-08 08:15:48

服務(wù)監(jiān)控Zipkin

2023-10-27 08:15:45

2024-05-22 09:45:49

2021-09-13 22:34:56

區(qū)塊鏈新基建數(shù)字化轉(zhuǎn)型

2023-03-06 21:29:41

mmap技術(shù)操作系統(tǒng)
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)