精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

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

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

一、介紹

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

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

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

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

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

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

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

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

二、crontab 定時(shí)器

2.1、介紹

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

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

簡而言之,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í)間定義大概是長下面這個(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腳本,過程如下!

  • 首先安裝 crond 相關(guān)服務(wù)
yum -y install cronie yum-cron
  • 編寫一個(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)容,說明運(yùn)行正常!

圖片圖片

  • 接著再來創(chuàng)建一個(gè)定時(shí)任務(wù),每分鐘執(zhí)行一次test.sh
#編輯定時(shí)任務(wù)【刪除-添加-修改】
crontab -e
  • 在文件末尾加入如下信息
#每分鐘執(zhí)行一次test.sh腳本
*/1 * * * * sh /root/shell/test.sh
  • 如果想查定時(shí)任務(wù)是否加入,可以通過如下命令
#查看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ù)日志,可通過如下命令進(jìn)行查看
tail -f /var/log/cron

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

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

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

3.1、Timer

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

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

實(shí)現(xià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)在開始 1 秒鐘之后,每隔 1 秒鐘執(zhí)行一次 job1
        timer.schedule(new TimerTest("job1"), delay1, period1);

        long delay2 = 2 * 1000;
        long period2 = 2 * 1000;
        // 從現(xiàn)在開始 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)在于簡單易用,由于所有任務(wù)都是由同一個(gè)線程來調(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í)間非常長的時(shí)候,會(huì)影響其他 job 的調(diào)度。例如,如果 job1 處理的時(shí)間要 1 分鐘, 那么 job2 至少要等 1 分鐘之后才能跑。

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

3.2、ScheduledExecutor

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

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

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

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)在開始1秒鐘之后,每隔1秒鐘執(zhí)行一次job1
        service.scheduleAtFixedRate(
                new ScheduledExecutorTest("job1"), initialDelay1,
                period1, TimeUnit.SECONDS);

        long initialDelay2 = 2;
        long delay2 = 2;
        // 從現(xiàn)在開始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)在開始多少TimeUnit后執(zhí)行任務(wù)
  • delay:表示任務(wù)執(zhí)行周期,每隔多少TimeUnit執(zhí)行一次任務(wù)

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

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

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

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

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

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

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

4.1、Quartz

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

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

  • 引入quartz包
<dependency>
    <groupId>org.quartz-scheduler</groupId>
    <artifactId>quartz</artifactId>
    <version>2.3.2</version>
</dependency>
  • 編寫示例代碼
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í)行類是QuartzTest, 指定一個(gè)K/V類型的數(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開始調(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ì)的核心類主要包括 Scheduler, Job 以及 Trigger。

  • Scheduler:可以理解為是一個(gè)具體的調(diào)度實(shí)例,用來調(diào)度任務(wù)
  • JobDetail:定義具體作業(yè)的實(shí)例,進(jìn)一步封裝和拓展了 Job 功能,其中 Job 是一個(gè)接口,類似上面的 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ù)也非常簡單。

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í)器開關(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的方式更加簡潔。

  • 實(shí)現(xiàn)過程如下
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)類添加@EnableScheduling,開啟定時(shí)任務(wù)功能
@SpringBootApplication
@EnableScheduling
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}
  • 編寫定時(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ī)則

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

@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ū)。可以設(shè)置為java.util.TimeZone中的zoneId
  • fixedDelay:從上一個(gè)任務(wù)完成開始到下一個(gè)任務(wù)開始的間隔,單位毫秒
  • fixedDelayString:同上,時(shí)間值是String類型
  • fixedRate:從上一個(gè)任務(wù)開始到下一個(gè)任務(wù)開始的間隔,單位毫秒
  • fixedRateString:同上,時(shí)間值是String類型
  • initialDelay:任務(wù)首次執(zhí)行延遲的時(shí)間,單位毫秒
  • initialDelayString:同上,時(shí)間值是String類型

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

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ū)分大小寫,更多配置可以參考這里。

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

圖片圖片

4.3、elastic-job

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

主要功能:

  • 分布式:重寫 Quartz 基于數(shù)據(jù)庫的分布式功能,改用 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ù)開始前觸發(fā)任務(wù)重分片。
  • 集中管理:采用基于Zookeeper的注冊(cè)中心,集中管理和協(xié)調(diào)分布式作業(yè)的狀態(tài),分配和監(jiān)聽。外部系統(tǒng)可直接根據(jù)Zookeeper的數(shù)據(jù)管理和監(jiān)控elastic-job。
  • 定制化流程型任務(wù):作業(yè)可分為簡單和數(shù)據(jù)流處理兩種模式,數(shù)據(jù)流又分為高吞吐處理模式和順序性處理模式,其中高吞吐處理模式可以開啟足夠多的線程快速的處理數(shù)據(jù),而順序性處理模式將每個(gè)分片項(xiàng)分配到一個(gè)獨(dú)立線程,用于保證同一分片的順序性,這點(diǎn)類似于kafka的分區(qū)順序性。

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

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

  • 引入相關(guān) elastic-job 依賴
<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類型配置
        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 就是作者名字的拼音首字母)的開源項(xiàng)目,官網(wǎng)上介紹這是一個(gè)輕量級(jí)分布式任務(wù)調(diào)度框架,其核心設(shè)計(jì)目標(biāo)是開發(fā)迅速、學(xué)習(xí)簡單、輕量級(jí)、易擴(kuò)展。

圖片圖片

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

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

  • 引入相關(guān) xxl-job 依賴
<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è)地址。從而更靈活的支持容器類型執(zhí)行器動(dòng)態(tài)IP和動(dòng)態(tài)映射端口問題。
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ǔ)磁盤路徑 [選填] :需要對(duì)該路徑擁有讀寫權(quán)限;為空則使用默認(rèn)路徑;
xxl.job.executor.logpath=/data/applogs/xxl-job/jobhandler
### 執(zhí)行器日志文件保存天數(shù) [選填] : 過期日志自動(dòng)清理, 限制值大于等于3時(shí)生效; 否則, 如-1, 關(guān)閉自動(dòng)清理功能;
xxl.job.executor.logretentinotallow=10
  • 編寫一個(gè)配置類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;
    }
}
  • 編寫具體任務(wù)類XxlJobDemoHandler
@Component
public class XxlJobDemoHandler {
    /**
     * Bean模式,一個(gè)方法為一個(gè)任務(wù)
     * 1、在Spring Bean實(shí)例中,開發(fā)Job方法,方式格式要求為 "public ReturnT<String> execute(String param)"
     * 2、為Job方法添加注解 "@XxlJob(value="自定義jobhandler名稱", init = "JobHandler初始化方法", destroy = "JobHandler銷毀方法")",注解value值對(duì)應(yīng)的是調(diào)度中心新建任務(wù)的JobHandler屬性的值。
     * 3、執(zhí)行日志:需要通過 "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;
    }
}

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

圖片圖片

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

圖片

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

圖片圖片

圖片圖片

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

圖片圖片

這就是簡單的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é)任編輯:武曉燕 來源: Java極客技術(shù)
相關(guān)推薦

2024-03-12 11:39:30

Python開發(fā)

2019-12-25 15:10:00

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

2025-07-02 04:00:00

2020-02-07 11:07:53

數(shù)組鏈表單鏈表

2023-11-20 08:18:49

Netty服務(wù)器

2022-12-20 07:39:46

2023-12-21 17:11:21

Containerd管理工具命令行

2023-07-31 08:18:50

Docker參數(shù)容器

2023-11-06 08:16:19

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

2021-05-29 10:11:00

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

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事件元素

2023-10-27 08:15:45

2023-11-08 08:15:48

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

2022-02-24 07:34:10

SSL協(xié)議加密

2024-02-19 00:00:00

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

2022-05-16 10:49:28

網(wǎng)絡(luò)協(xié)議數(shù)據(jù)

2023-03-06 21:29:41

mmap技術(shù)操作系統(tǒng)

2022-04-08 09:01:14

CSS自定義屬性前端
點(diǎn)贊
收藏

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

91在线导航| 一级做a爰片久久毛片| 欧美视频精品全部免费观看| 一区二区三区精品久久久| 成人免费在线视频网址| 久久久久无码精品国产| 伊人久久大香线蕉综合网蜜芽| 欧美手机在线视频| 日本福利视频在线观看| 免费国产精品视频| 免费视频最近日韩| 欧美日本高清一区| 天天躁日日躁aaaxxⅹ| 四虎影视精品永久在线观看| 亚洲国产综合人成综合网站| 日本日本精品二区免费| a在线观看视频| 久久综合图片| 久久久久久久久综合| 亚洲一区二区自偷自拍 | 欧美日韩另类字幕中文| 亚洲综合首页| 精品亚洲综合| 成人永久免费视频| 国产精品欧美激情| 国产午夜视频在线| 欧美国产小视频| 亚洲欧美精品suv| gogo亚洲国模私拍人体| 91国拍精品国产粉嫩亚洲一区 | 伊人伊成久久人综合网站| 国产人妻精品午夜福利免费| 亚洲成人va| 天天做天天摸天天爽国产一区| 日本黄色a视频| 国产毛片在线看| va亚洲va日韩不卡在线观看| 成人亚洲激情网| 少妇又紧又色又爽又刺激视频| 91久久在线| 久久99青青精品免费观看| 亚洲女同二女同志奶水| 国产一区二区三区网| 亚洲精品99久久久久中文字幕| 一级黄色大片儿| 日韩五码电影| 欧美日韩视频一区二区| 国产免费成人在线| 精精国产xxxx视频在线野外| 亚洲一区二区四区蜜桃| 狠狠干视频网站| 1769视频在线播放免费观看| 国产日韩精品久久久| 精品在线一区| 秋霞av在线| 久久综合丝袜日本网| 精品国产区在线| 五月婷婷深深爱| 99riav一区二区三区| 激情小说网站亚洲综合网 | 亚洲美女精品久久| 国产 中文 字幕 日韩 在线| 蜜桃久久久久| 日韩久久免费电影| 免费看污黄网站在线观看| 亚洲老女人视频免费| 亚洲福利小视频| 99久久免费看精品国产一区| 欧美日韩看看2015永久免费| 亚洲精品久久久久| 玖玖爱在线观看| 欧美理论视频| 日韩视频免费中文字幕| 日本黄色片免费观看| 午夜天堂精品久久久久| 高清欧美电影在线| 毛片视频网站在线观看| 久久xxxx精品视频| 国产成人在线一区二区| 中文字幕理论片| 韩国v欧美v日本v亚洲v| 99蜜桃在线观看免费视频网站| 欧美特黄一级视频| 久久久久青草大香线综合精品| 日韩欧美激情一区二区| 日本三级视频在线观看| 一个色综合av| 欧美三级午夜理伦三级| 日日狠狠久久| 亚洲高清在线观看| 一级特黄曰皮片视频| 一区二区三区中文| 57pao成人国产永久免费| 欧美在线视频精品| 国产很黄免费观看久久| 久久99影院| 最新真实国产在线视频| 一区二区三区精密机械公司| 国产精品亚洲a| 久久精品97| 亚洲国产精品视频在线观看| 国产又粗又黄又猛| 激情综合视频| 国产免费亚洲高清| 黑人精品一区二区| 国产精品亲子乱子伦xxxx裸| 中文字幕无码精品亚洲资源网久久| 欧美一区国产| 日韩欧美国产高清| 日本高清www| 欧美日本一区二区视频在线观看| 欧美综合在线第二页| av手机免费看| 国产欧美视频一区二区三区| av无码久久久久久不卡网站| 草莓视频成人appios| 亚洲国产成人精品女人久久久| 欧美美女性生活视频| 久久天堂精品| 国产精品v欧美精品∨日韩| jizzjizz在线观看| 岛国av一区二区三区| 欧美人与性动交α欧美精品| 日韩成人三级| 欧洲亚洲妇女av| 黄频网站在线观看| 亚洲欧洲av一区二区三区久久| av动漫在线观看| 风间由美中文字幕在线看视频国产欧美| 中文字幕在线精品| 无码人妻久久一区二区三区| 99视频国产精品| 成人午夜免费在线视频| 精品麻豆剧传媒av国产九九九| 在线日韩中文字幕| 亚洲第一网站在线观看| 97久久精品人人做人人爽50路| 日本高清视频免费在线观看| 99热这里有精品| 中文字幕亚洲一区在线观看| 久久影视中文字幕| 久久久蜜桃精品| 黄色片视频在线免费观看| 亚洲高清999| 欧美成人免费在线观看| 国产女人18毛片18精品| 亚洲欧洲日韩av| 亚欧激情乱码久久久久久久久| 欧美日韩一二三四| 国产精品久久久久久超碰| 欧美少妇另类| 日韩欧美视频一区二区三区| 麻豆国产精品一区| 国产亚洲高清视频| 久久精品综合一区| 是的av在线| 亚洲一区第一页| 国产成人av免费| 国产精品日日摸夜夜摸av| 亚洲77777| 日韩在线综合| 91视频免费网站| av毛片在线播放| 欧美xxxxxxxxx| 久草国产精品视频| 99国产欧美另类久久久精品| 欧美 日韩 国产一区| 国产一区二区三区电影在线观看| 国产精品白嫩初高中害羞小美女| 国产美女性感在线观看懂色av| 精品视频一区 二区 三区| 羞羞在线观看视频| 国产精品一品二品| 人妻少妇精品无码专区二区| 香蕉视频一区| 国产精品人成电影| 中文字幕在线观看网站| 亚洲精品电影在线观看| 91麻豆精品在线| 日韩美女视频一区二区| 欧美丰满熟妇bbb久久久| 国产一区二区精品| 亚洲国产一区二区在线| 欧美经典一区| 国产999在线观看| 欧美极品视频| 欧美精品一区视频| 无码人妻av免费一区二区三区 | 精品3atv在线视频| 久久视频这里只有精品| 黄色www视频| 色94色欧美sute亚洲13| 国产免费久久久久| 91偷拍与自偷拍精品| 日本在线一二三区| 国模吧视频一区| 色一情一乱一伦一区二区三区| 天天综合91| 91高清视频免费观看| 日韩精品毛片| 国产视频一区在线| 99久久久久成人国产免费 | 亚洲天堂777| 亚洲图片欧美视频| a级黄色免费视频| 99久久综合99久久综合网站| 中文字幕66页| 久久国产福利| www.欧美黄色| 日韩精品二区| 久久香蕉综合色| 亚洲3区在线| 国产免费亚洲高清| 日韩电影免费观看高清完整版| 欧美成人精品在线播放| 99中文字幕一区| 日韩av一卡二卡| www.五月婷| 欧美日韩精品福利| 日本天堂网在线| 亚洲国产日韩a在线播放性色| 五月天婷婷丁香网| 国产午夜精品理论片a级大结局| 欧美久久久久久久久久久| 精品一区二区三区在线观看国产| 精品一区二区中文字幕| 伊人成年综合电影网| 国产香蕉一区二区三区| 成人在线免费视频观看| 欧美不卡三区| 五月天亚洲一区| 国产伦精品一区二区三区在线| 国产va免费精品观看精品| 国产精品∨欧美精品v日韩精品| 精精国产xxxx视频在线播放| 久久久免费在线观看| 色帝国亚洲欧美在线| 久久精品视频中文字幕| 欧美日韩在线看片| 一区二区三区天堂av| 激情在线视频| 亚洲精品自拍偷拍| 天堂在线中文| 日韩高清av一区二区三区| 五月天激情开心网| 精品国产凹凸成av人导航| 亚洲国产精品久久人人爱潘金莲| 欧美一卡2卡3卡4卡| 国产情侣激情自拍| 91精品国产色综合久久ai换脸| 国产精品一品二区三区的使用体验| 欧美性做爰猛烈叫床潮| 一区二区三区在线免费观看视频 | aa视频在线免费观看| 欧美日韩高清一区| 国产精品女人久久久| 制服丝袜亚洲色图| av网站在线免费看| 精品精品欲导航| 蜜臀av在线观看| 日韩av网站导航| 精品欧美不卡一区二区在线观看| 亚洲欧美在线磁力| av大片在线观看| 久久色精品视频| 性欧美ⅴideo另类hd| 久久久久久久久久国产| 不卡专区在线| 国产v综合v亚洲欧美久久| 欧美xnxx| 91最新在线免费观看| 91亚洲无吗| 久久综合色一本| 日本在线电影一区二区三区| 中文字幕制服丝袜在线| 一本精品一区二区三区| 欧美精品久久久久久久久久久| 国产一区二区三区成人欧美日韩在线观看 | 少妇人妻偷人精品一区二区| 亚洲精品之草原avav久久| 午夜视频在线看| 欧美激情第三页| 亚洲欧洲日本韩国| 国产在线播放91| 综合激情网...| 欧美久久综合性欧美| 97在线精品| 男人添女荫道口图片| 久久久国产亚洲精品| 一级淫片在线观看| 99视频国产精品| 91久久久久久久久久久久久久| 亚洲激情网站免费观看| 99超碰在线观看| 91精品国产综合久久久蜜臀粉嫩| 人妻视频一区二区三区| 中文字幕日韩欧美| 国产美女一区视频| 国产精品视频播放| 亚洲精品一区国产| 日韩hmxxxx| 欧美福利在线| aaaaaa亚洲| 国产成人免费视| 毛片aaaaaa| 五月婷婷激情综合网| 国产一区二区三区在线观看| 日韩av一卡二卡| 在线观看操人| 国产精品一久久香蕉国产线看观看| 一区二区三区自拍视频| 日韩一区二区电影在线观看| 激情综合中文娱乐网| 在线观看免费不卡av| 97se亚洲国产综合自在线不卡 | 青草久久伊人| 欧美国产日韩一区二区| 台湾天天综合人成在线| 明星裸体视频一区二区| 韩日视频一区| 天天做天天干天天操| 亚洲国产岛国毛片在线| 日韩精品久久久久久免费| 亚洲成人精品久久| 69xxx在线| 91免费在线视频| 日韩一区三区| 91最新在线观看| 久久蜜桃av一区精品变态类天堂 | 亚洲在线第一页| 久久免费大视频| 久久精品香蕉视频| 99久久免费视频.com| 久草国产在线观看| 日韩一区二区三区观看| 麻豆视频在线免费观看| 国产美女久久久| 日韩www.| 久久99爱视频| 国产精品久久久久aaaa| 羞羞色院91蜜桃| 一区二区三区回区在观看免费视频| 欧美激情网站| 久久久婷婷一区二区三区不卡| 亚洲国产1区| 欧美性生交xxxxx| 亚洲国产精品久久人人爱| 亚洲乱码在线观看| 美女精品久久久| 欧美国产中文高清| www.av91| aaa欧美日韩| 四虎成人永久免费视频| 日韩精品欧美国产精品忘忧草| 国产h片在线观看| 国产精品二区在线| 亚洲激情婷婷| 黄瓜视频污在线观看| 一本到不卡精品视频在线观看| 九色蝌蚪在线| 国产精品最新在线观看| 亚洲国产精品久久久久蝴蝶传媒| www.五月天色| 亚洲午夜羞羞片| 午夜在线视频观看| 国产福利视频一区| 99精品国产一区二区三区| 国内精品国产三级国产aⅴ久| 一卡二卡欧美日韩| 奇米影视888狠狠狠777不卡| 国产精品久久久久福利| 亚洲草久电影| 中国特级黄色大片| 色综合中文字幕| av在线免费观看网站| 91中文字幕在线| 国产精品视区| 日本高清黄色片| 日韩一二三区不卡| 天堂网在线最新版www中文网| 日韩免费三级| 国产·精品毛片| 久久久久久少妇| 啊v视频在线一区二区三区 | 日本一区二区三区免费观看| 蜜桃传媒麻豆第一区在线观看| 欧美黄色一级网站| 亚洲免费av片| 精品一区二区三区免费看| 99热自拍偷拍| 一区在线观看视频| 五月天婷婷在线观看| 成人国产精品久久久| 日韩亚洲国产精品| 波多野结衣家庭教师在线观看| 精品国精品自拍自在线| 国产69精品久久久久9999人| 国产一区二区三区在线免费| 国产婷婷色一区二区三区四区 | www.日本精品|