国产秋霞理论久久久电影-婷婷色九月综合激情丁香-欧美在线观看乱妇视频-精品国avA久久久久久久-国产乱码精品一区二区三区亚洲人-欧美熟妇一区二区三区蜜桃视频

任務(wù)調(diào)度框架 Quartz 用法指南「超詳細(xì)」

共 45306字,需瀏覽 91分鐘

 ·

2022-05-27 14:14

????關(guān)注后回復(fù) “進(jìn)群” ,拉你進(jìn)程序員交流群????

大家好,我是胖虎!

今天早上,見交流群的小伙伴兒正在熱火朝天的討論著各種實(shí)現(xiàn)自定義定時(shí)任務(wù)的方案,從Quartz到Xxl-job,再到Elastic-job,能聊的都聊了一圈兒;剛剛好手頭有一份關(guān)于 Quartz 的保姆級(jí)教程,在這里分享給大家;

1前言

項(xiàng)目中遇到一個(gè),需要 客戶自定任務(wù)啟動(dòng)時(shí)間 的需求。原來(lái)一直都是在項(xiàng)目里硬編碼一些定時(shí)器,所以沒有學(xué)習(xí)過。

很多開源的項(xiàng)目管理框架都已經(jīng)做了 Quartz 的集成。我們居然連這么常用得東西居然沒有做成模塊化,實(shí)在是不應(yīng)該。

Quartz是OpenSymphony開源組織在Job scheduling領(lǐng)域又一個(gè)開源項(xiàng)目,完全由Java開發(fā),可以用來(lái)執(zhí)行定時(shí)任務(wù),類似于java.util.Timer。但是相較于Timer, Quartz增加了很多功能:

  • 持久性作業(yè) - 就是保持調(diào)度定時(shí)的狀態(tài);
  • 作業(yè)管理 - 對(duì)調(diào)度作業(yè)進(jìn)行有效的管理;

官方文檔:

  • http://www.quartz-scheduler.org/documentation/
  • http://www.quartz-scheduler.org/api/2.3.0/index.html

2基礎(chǔ)使用

Quartz 的核心類有以下三部分:

  • 任務(wù) Job : 需要實(shí)現(xiàn)的任務(wù)類,實(shí)現(xiàn) execute() 方法,執(zhí)行后完成任務(wù)。
  • 觸發(fā)器 Trigger : 包括 SimpleTriggerCronTrigger。
  • 調(diào)度器 Scheduler : 任務(wù)調(diào)度器,負(fù)責(zé)基于 Trigger觸發(fā)器,來(lái)執(zhí)行 Job任務(wù)。

主要關(guān)系如下:

3Demo

按照官網(wǎng)的 Demo,搭建一個(gè)純 maven 項(xiàng)目,添加依賴:

<!-- 核心包 -->
<dependency>
    <groupId>org.quartz-scheduler</groupId>
    <artifactId>quartz</artifactId>
    <version>2.3.0</version>
</dependency>
<!-- 工具包 -->
<dependency>
    <groupId>org.quartz-scheduler</groupId>
    <artifactId>quartz-jobs</artifactId>
    <version>2.3.0</version>
</dependency>

新建一個(gè)任務(wù),實(shí)現(xiàn)了 org.quartz.Job 接口:

public class MyJob implements Job {
    @Override
    public void execute(JobExecutionContext context) throws JobExecutionException {
        System.out.println("任務(wù)被執(zhí)行了。。。");
    }
}

main 方法,創(chuàng)建調(diào)度器、jobDetail 實(shí)例、trigger 實(shí)例、執(zhí)行:

public static void main(String[] args) throws Exception {
    // 1.創(chuàng)建調(diào)度器 Scheduler
    SchedulerFactory factory = new StdSchedulerFactory();
    Scheduler scheduler = factory.getScheduler();

    // 2.創(chuàng)建JobDetail實(shí)例,并與MyJob類綁定(Job執(zhí)行內(nèi)容)
    JobDetail job = JobBuilder.newJob(MyJob.class)
        .withIdentity("job1", "group1")
        .build()
;

    // 3.構(gòu)建Trigger實(shí)例,每隔30s執(zhí)行一次
    Trigger trigger = TriggerBuilder.newTrigger()
        .withIdentity("trigger1""group1")
        .startNow()
        .withSchedule(simpleSchedule()
                      .withIntervalInSeconds(30)
                      .repeatForever())
        .build();

    // 4.執(zhí)行,開啟調(diào)度器
    scheduler.scheduleJob(job, trigger);
    System.out.println(System.currentTimeMillis());
    scheduler.start();

    //主線程睡眠1分鐘,然后關(guān)閉調(diào)度器
    TimeUnit.MINUTES.sleep(1);
    scheduler.shutdown();
    System.out.println(System.currentTimeMillis());
}

日志打印情況:

4JobDetail

JobDetail 的作用是綁定 Job,是一個(gè)任務(wù)實(shí)例,它為 Job 添加了許多擴(kuò)展參數(shù)。

每次Scheduler調(diào)度執(zhí)行一個(gè)Job的時(shí)候,首先會(huì)拿到對(duì)應(yīng)的Job,然后創(chuàng)建該Job實(shí)例,再去執(zhí)行Job中的execute()的內(nèi)容,任務(wù)執(zhí)行結(jié)束后,關(guān)聯(lián)的Job對(duì)象實(shí)例會(huì)被釋放,且會(huì)被JVM GC清除。

為什么設(shè)計(jì)成JobDetail + Job,不直接使用Job?

JobDetail 定義的是任務(wù)數(shù)據(jù),而真正的執(zhí)行邏輯是在Job中。

這是因?yàn)槿蝿?wù)是有可能并發(fā)執(zhí)行,如果Scheduler直接使用Job,就會(huì)存在對(duì)同一個(gè)Job實(shí)例并發(fā)訪問的問題。

JobDetail & Job 方式,Sheduler每次執(zhí)行,都會(huì)根據(jù)JobDetail創(chuàng)建一個(gè)新的Job實(shí)例,這樣就可以 規(guī)避并發(fā)訪問 的問題。

5JobExecutionContext

  • 當(dāng) Scheduler 調(diào)用一個(gè) job,就會(huì)將 JobExecutionContext 傳遞給 Job 的 execute() 方法;
  • Job 能通過 JobExecutionContext 對(duì)象訪問到 Quartz 運(yùn)行時(shí)候的環(huán)境以及 Job 本身的明細(xì)數(shù)據(jù)。

任務(wù)實(shí)現(xiàn)的 execute() 方法,可以通過 context 參數(shù)獲取。

public interface Job {
    void execute(JobExecutionContext context)
        throws JobExecutionException
;
}

Builder 建造過程中,可以使用如下方法:

usingJobData("tiggerDataMap""測(cè)試傳參")

execute 方法中獲?。?/p>

context.getTrigger().getJobDataMap().get("tiggerDataMap");
context.getJobDetail().getJobDataMap().get("tiggerDataMap");

6Job 狀態(tài)參數(shù)

有狀態(tài)的 job 可以理解為多次 job調(diào)用期間可以持有一些狀態(tài)信息,這些狀態(tài)信息存儲(chǔ)在 JobDataMap 中。

而默認(rèn)的無(wú)狀態(tài) job,每次調(diào)用時(shí)都會(huì)創(chuàng)建一個(gè)新的 JobDataMap。

示例如下:

//多次調(diào)用 Job 的時(shí)候,將參數(shù)保留在 JobDataMap
@PersistJobDataAfterExecution
public class JobStatus implements Job {
    @Override
    public void execute(JobExecutionContext context) throws JobExecutionException {
        long count = (long) context.getJobDetail().getJobDataMap().get("count");
        System.out.println("當(dāng)前執(zhí)行,第" + count + "次");
        context.getJobDetail().getJobDataMap().put("count", ++count);
    }
}
JobDetail job = JobBuilder.newJob(JobStatus.class)
                .withIdentity("statusJob", "group1")
                .usingJobData("count", 1L)
                .build()
;

輸出結(jié)果:

當(dāng)前執(zhí)行,第1
[main] INFO org.quartz.core.QuartzScheduler - Scheduler DefaultQuartzScheduler_$_NON_CLUSTERED started.
當(dāng)前執(zhí)行,第2
當(dāng)前執(zhí)行,第3

7Trigger

定時(shí)啟動(dòng)/關(guān)閉

Trigger 可以設(shè)置任務(wù)的開始結(jié)束時(shí)間, Scheduler 會(huì)根據(jù)參數(shù)進(jìn)行觸發(fā)。

Calendar instance = Calendar.getInstance();
Date startTime = instance.getTime();
instance.add(Calendar.MINUTE, 1);
Date endTime = instance.getTime();

// 3.構(gòu)建Trigger實(shí)例
Trigger trigger = TriggerBuilder.newTrigger()
    .withIdentity("trigger1""group1")
    // 開始時(shí)間
    .startAt(startTime)
    // 結(jié)束時(shí)間
    .endAt(endTime)
    .build();

在 job 中也能拿到對(duì)應(yīng)的時(shí)間,并進(jìn)行業(yè)務(wù)判斷

public void execute(JobExecutionContext context) throws JobExecutionException {
    System.out.println("任務(wù)執(zhí)行。。。");
    System.out.println(context.getTrigger().getStartTime());
    System.out.println(context.getTrigger().getEndTime());
}

運(yùn)行結(jié)果:

[main] INFO org.quartz.impl.StdSchedulerFactory - Quartz scheduler version: 2.3.0
1633149326723
任務(wù)執(zhí)行。。。
Sat Oct 02 12:35:26 CST 2021
Sat Oct 02 12:36:26 CST 2021
[main] INFO org.quartz.core.QuartzScheduler - Scheduler DefaultQuartzScheduler_$_NON_CLUSTERED started.

8SimpleTrigger

這是比較簡(jiǎn)單的一類觸發(fā)器,用它能實(shí)現(xiàn)很多基礎(chǔ)的應(yīng)用。使用它的主要場(chǎng)景包括:

  • 在指定時(shí)間段內(nèi),執(zhí)行一次任務(wù)

    最基礎(chǔ)的 Trigger 不設(shè)置循環(huán),設(shè)置開始時(shí)間。

  • 在指定時(shí)間段內(nèi),循環(huán)執(zhí)行任務(wù)

    在 1 基礎(chǔ)上加上循環(huán)間隔??梢灾付?永遠(yuǎn)循環(huán)、運(yùn)行指定次數(shù)

    TriggerBuilder.newTrigger()
        .withSchedule(SimpleScheduleBuilder
                      .simpleSchedule()
                      .withIntervalInSeconds(30)
                      .repeatForever())

    withRepeatCount(count) 是重復(fù)次數(shù),實(shí)際運(yùn)行次數(shù)為 count+1

    TriggerBuilder.newTrigger()
        .withSchedule(SimpleScheduleBuilder
                      .simpleSchedule()
                      .withIntervalInSeconds(30)
                      .withRepeatCount(5))
  • 立即開始,指定時(shí)間結(jié)束

    這個(gè),略。

9CronTrigger

CronTrigger 是基于日歷的任務(wù)調(diào)度器,在實(shí)際應(yīng)用中更加常用。

雖然很常用,但是知識(shí)點(diǎn)都一樣,只是可以通過表達(dá)式來(lái)設(shè)置時(shí)間而已。

使用方式就是綁定調(diào)度器時(shí)換一下:

TriggerBuilder.newTrigger().withSchedule(CronScheduleBuilder.cronSchedule("* * * * * ?"))

Cron 表達(dá)式這里不介紹,貼個(gè)圖跳過

順便推薦一個(gè)非常好用的Cron 表達(dá)式在線生成,反解析的工具:www.matools.com/cron 非常好用,點(diǎn)幾下,就能得到自己想要的cron表達(dá)式;

10SpringBoot 整合

下面集成應(yīng)用截圖來(lái)自 Ruoyi 框架:

從上面的截圖中,可以看到這個(gè)定時(shí)任務(wù)模塊實(shí)現(xiàn)了:

  • cron表達(dá)式定時(shí)執(zhí)行
  • 并發(fā)執(zhí)行
  • 錯(cuò)誤策略
  • 啟動(dòng)執(zhí)行、暫停執(zhí)行

如果再加上:設(shè)置啟動(dòng)時(shí)間、停止時(shí)間 就更好了。不過啟停時(shí)間只是調(diào)用兩個(gè)方法而已,也就不寫了。

這一部分就主要是看 RuoYi 框架 代碼,然后加一點(diǎn)我需要用的功能。

前端部分就不寫了,全部用 swagger 代替,一些基礎(chǔ)字段也刪除了,僅復(fù)制主體功能。

已完成代碼示例:

https://gitee.com/qianwei4712/code-of-shiva/tree/master/quartz

11環(huán)境準(zhǔn)備

從 springboot 2.4.10 開始,添加 quartz 的 maven 依賴:

<!--  Quartz 任務(wù)調(diào)度 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-quartz</artifactId>
</dependency>

application 配置文件:

# 開發(fā)環(huán)境配置
server:
  # 服務(wù)器的HTTP端口
  port: 80
  servlet:
    # 應(yīng)用的訪問路徑
    context-path: /
  tomcat:
    # tomcat的URI編碼
    uri-encoding: UTF-8

spring:
  datasource:
    username: root
    password: root
    url: jdbc:mysql://127.0.0.1:3306/quartz?useUnicode=true&characterEncoding=utf-8&useSSL=true
    driver-class-name: com.mysql.cj.jdbc.Driver

    # HikariPool 較佳配置
    hikari:
      # 客戶端(即您)等待來(lái)自池的連接的最大毫秒數(shù)
      connection-timeout: 60000
      # 控制將測(cè)試連接的活動(dòng)性的最長(zhǎng)時(shí)間
      validation-timeout: 3000
      # 控制允許連接在池中保持空閑狀態(tài)的最長(zhǎng)時(shí)間
      idle-timeout: 60000

      login-timeout: 5
      # 控制池中連接的最大生存期
      max-lifetime: 60000
      # 控制允許池達(dá)到的最大大小,包括空閑和使用中的連接
      maximum-pool-size: 10
      # 控制HikariCP嘗試在池中維護(hù)的最小空閑連接數(shù)
      minimum-idle: 10
      # 控制默認(rèn)情況下從池獲得的連接是否處于只讀模式
      read-only: false

Quartz 自帶有數(shù)據(jù)庫(kù)模式,腳本都是現(xiàn)成的:

下載這個(gè)腳本:

https://gitee.com/qianwei4712/code-of-shiva/blob/master/quartz/quartz.sql

保存任務(wù)的數(shù)據(jù)庫(kù)表:

CREATE TABLE `quartz_job` (
  `job_id` bigint(20NOT NULL AUTO_INCREMENT COMMENT '任務(wù)ID',
  `job_name` varchar(64NOT NULL DEFAULT '' COMMENT '任務(wù)名稱',
  `job_group` varchar(64NOT NULL DEFAULT 'DEFAULT' COMMENT '任務(wù)組名',
  `invoke_target` varchar(500NOT NULL COMMENT '調(diào)用目標(biāo)字符串',
  `cron_expression` varchar(255DEFAULT '' COMMENT 'cron執(zhí)行表達(dá)式',
  `misfire_policy` varchar(20DEFAULT '3' COMMENT '計(jì)劃執(zhí)行錯(cuò)誤策略(1立即執(zhí)行 2執(zhí)行一次 3放棄執(zhí)行)',
  `concurrent` char(1DEFAULT '1' COMMENT '是否并發(fā)執(zhí)行(0允許 1禁止)',
  `status` char(1DEFAULT '0' COMMENT '狀態(tài)(0正常 1暫停)',
  `remark` varchar(500DEFAULT '' COMMENT '備注信息',
  PRIMARY KEY (`job_id`,`job_name`,`job_group`)
ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8 COMMENT='定時(shí)任務(wù)調(diào)度表';

最后準(zhǔn)備一個(gè)任務(wù)方法:

@Slf4j
@Component("mysqlJob")
public class MysqlJob {
    protected final Logger logger = LoggerFactory.getLogger(this.getClass());
    public void execute(String param) {
        logger.info("執(zhí)行 Mysql Job,當(dāng)前時(shí)間:{},任務(wù)參數(shù):{}", LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")), param);
    }
}

12核心代碼

ScheduleConfig 配置代碼類:

@Configuration
public class ScheduleConfig {

    @Bean
    public SchedulerFactoryBean schedulerFactoryBean(DataSource dataSource) {
        SchedulerFactoryBean factory = new SchedulerFactoryBean();
        factory.setDataSource(dataSource);

        // quartz參數(shù)
        Properties prop = new Properties();
        prop.put("org.quartz.scheduler.instanceName""shivaScheduler");
        prop.put("org.quartz.scheduler.instanceId""AUTO");
        // 線程池配置
        prop.put("org.quartz.threadPool.class""org.quartz.simpl.SimpleThreadPool");
        prop.put("org.quartz.threadPool.threadCount""20");
        prop.put("org.quartz.threadPool.threadPriority""5");
        // JobStore配置
        prop.put("org.quartz.jobStore.class""org.quartz.impl.jdbcjobstore.JobStoreTX");
        // 集群配置
        prop.put("org.quartz.jobStore.isClustered""true");
        prop.put("org.quartz.jobStore.clusterCheckinInterval""15000");
        prop.put("org.quartz.jobStore.maxMisfiresToHandleAtATime""1");
        prop.put("org.quartz.jobStore.txIsolationLevelSerializable""true");

        // sqlserver 啟用
        // prop.put("org.quartz.jobStore.selectWithLockSQL", "SELECT * FROM {0}LOCKS UPDLOCK WHERE LOCK_NAME = ?");
        prop.put("org.quartz.jobStore.misfireThreshold""12000");
        prop.put("org.quartz.jobStore.tablePrefix""QRTZ_");
        factory.setQuartzProperties(prop);

        factory.setSchedulerName("shivaScheduler");
        // 延時(shí)啟動(dòng)
        factory.setStartupDelay(1);
        factory.setApplicationContextSchedulerContextKey("applicationContextKey");
        // 可選,QuartzScheduler
        // 啟動(dòng)時(shí)更新己存在的Job,這樣就不用每次修改targetObject后刪除qrtz_job_details表對(duì)應(yīng)記錄了
        factory.setOverwriteExistingJobs(true);
        // 設(shè)置自動(dòng)啟動(dòng),默認(rèn)為true
        factory.setAutoStartup(true);

        return factory;
    }
}

ScheduleUtils 調(diào)度工具類,這是本篇中最核心的代碼:

public class ScheduleUtils {
    /**
     * 得到quartz任務(wù)類
     *
     * @param job 執(zhí)行計(jì)劃
     * @return 具體執(zhí)行任務(wù)類
     */

    private static Class<? extends Job> getQuartzJobClass(QuartzJob job) {
        boolean isConcurrent = "0".equals(job.getConcurrent());
        return isConcurrent ? QuartzJobExecution.class : QuartzDisallowConcurrentExecution.class;
    }

    /**
     * 構(gòu)建任務(wù)觸發(fā)對(duì)象
     */

    public static TriggerKey getTriggerKey(Long jobId, String jobGroup) {
        return TriggerKey.triggerKey(ScheduleConstants.TASK_CLASS_NAME + jobId, jobGroup);
    }

    /**
     * 構(gòu)建任務(wù)鍵對(duì)象
     */

    public static JobKey getJobKey(Long jobId, String jobGroup) {
        return JobKey.jobKey(ScheduleConstants.TASK_CLASS_NAME + jobId, jobGroup);
    }

    /**
     * 創(chuàng)建定時(shí)任務(wù)
     */

    public static void createScheduleJob(Scheduler scheduler, QuartzJob job) throws Exception {
        Class<? extends Job> jobClass = getQuartzJobClass(job);
        // 構(gòu)建job信息
        Long jobId = job.getJobId();
        String jobGroup = job.getJobGroup();
        JobDetail jobDetail = JobBuilder.newJob(jobClass).withIdentity(getJobKey(jobId, jobGroup)).build();

        // 表達(dá)式調(diào)度構(gòu)建器
        CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(job.getCronExpression());
        cronScheduleBuilder = handleCronScheduleMisfirePolicy(job, cronScheduleBuilder);

        // 按新的cronExpression表達(dá)式構(gòu)建一個(gè)新的trigger
        CronTrigger trigger = TriggerBuilder.newTrigger().withIdentity(getTriggerKey(jobId, jobGroup))
                .withSchedule(cronScheduleBuilder).build();

        // 放入?yún)?shù),運(yùn)行時(shí)的方法可以獲取
        jobDetail.getJobDataMap().put(ScheduleConstants.TASK_PROPERTIES, job);

        // 判斷是否存在
        if (scheduler.checkExists(getJobKey(jobId, jobGroup))) {
            // 防止創(chuàng)建時(shí)存在數(shù)據(jù)問題 先移除,然后在執(zhí)行創(chuàng)建操作
            scheduler.deleteJob(getJobKey(jobId, jobGroup));
        }

        scheduler.scheduleJob(jobDetail, trigger);

        // 暫停任務(wù)
        if (job.getStatus().equals(ScheduleConstants.Status.PAUSE.getValue())) {
            scheduler.pauseJob(ScheduleUtils.getJobKey(jobId, jobGroup));
        }
    }

    /**
     * 設(shè)置定時(shí)任務(wù)策略
     */

    public static CronScheduleBuilder handleCronScheduleMisfirePolicy(QuartzJob job, CronScheduleBuilder cb)
            throws Exception 
{
        switch (job.getMisfirePolicy()) {
            case ScheduleConstants.MISFIRE_DEFAULT:
                return cb;
            case ScheduleConstants.MISFIRE_IGNORE_MISFIRES:
                return cb.withMisfireHandlingInstructionIgnoreMisfires();
            case ScheduleConstants.MISFIRE_FIRE_AND_PROCEED:
                return cb.withMisfireHandlingInstructionFireAndProceed();
            case ScheduleConstants.MISFIRE_DO_NOTHING:
                return cb.withMisfireHandlingInstructionDoNothing();
            default:
                throw new Exception("The task misfire policy '" + job.getMisfirePolicy()
                        + "' cannot be used in cron schedule tasks");
        }
    }
}

這里可以看到,在完成任務(wù)與觸發(fā)器的關(guān)聯(lián)后,如果是暫停狀態(tài),會(huì)先讓調(diào)度器停止任務(wù)。

AbstractQuartzJob 抽象任務(wù):

public abstract class AbstractQuartzJob implements Job {
    private static final Logger log = LoggerFactory.getLogger(AbstractQuartzJob.class);

    /**
     * 線程本地變量
     */

    private static ThreadLocal<Date> threadLocal = new ThreadLocal<>();

    @Override
    public void execute(JobExecutionContext context) throws JobExecutionException {
        QuartzJob job = new QuartzJob();
        BeanUtils.copyBeanProp(job, context.getMergedJobDataMap().get(ScheduleConstants.TASK_PROPERTIES));
        try {
            before(context, job);
            if (job != null) {
                doExecute(context, job);
            }
            after(context, job, null);
        } catch (Exception e) {
            log.error("任務(wù)執(zhí)行異常  - :", e);
            after(context, job, e);
        }
    }

    /**
     * 執(zhí)行前
     *
     * @param context 工作執(zhí)行上下文對(duì)象
     * @param job     系統(tǒng)計(jì)劃任務(wù)
     */

    protected void before(JobExecutionContext context, QuartzJob job) {
        threadLocal.set(new Date());
    }

    /**
     * 執(zhí)行后
     *
     * @param context 工作執(zhí)行上下文對(duì)象
     * @param sysJob  系統(tǒng)計(jì)劃任務(wù)
     */

    protected void after(JobExecutionContext context, QuartzJob sysJob, Exception e) {

    }

    /**
     * 執(zhí)行方法,由子類重載
     *
     * @param context 工作執(zhí)行上下文對(duì)象
     * @param job     系統(tǒng)計(jì)劃任務(wù)
     * @throws Exception 執(zhí)行過程中的異常
     */

    protected abstract void doExecute(JobExecutionContext context, QuartzJob job) throws Exception;
}

這個(gè)類將原本 execute 方法執(zhí)行的任務(wù),下放到了子類重載的 doExecute 方法中

同時(shí)準(zhǔn)備實(shí)現(xiàn),分了允許并發(fā)和不允許并發(fā),差別就是一個(gè)注解:

public class QuartzJobExecution extends AbstractQuartzJob {
    @Override
    protected void doExecute(JobExecutionContext context, QuartzJob job) throws Exception {
        JobInvokeUtil.invokeMethod(job);
    }
}
@DisallowConcurrentExecution
public class QuartzDisallowConcurrentExecution extends AbstractQuartzJob {
    @Override
    protected void doExecute(JobExecutionContext context, QuartzJob job) throws Exception {
        JobInvokeUtil.invokeMethod(job);
    }
}

最后由 JobInvokeUtil 通過反射,進(jìn)行實(shí)際的方法調(diào)用:

public class JobInvokeUtil {
    /**
     * 執(zhí)行方法
     *
     * @param job 系統(tǒng)任務(wù)
     */

    public static void invokeMethod(QuartzJob job) throws Exception {
        String invokeTarget = job.getInvokeTarget();
        String beanName = getBeanName(invokeTarget);
        String methodName = getMethodName(invokeTarget);
        List<Object[]> methodParams = getMethodParams(invokeTarget);

        if (!isValidClassName(beanName)) {
            Object bean = SpringUtils.getBean(beanName);
            invokeMethod(bean, methodName, methodParams);
        } else {
            Object bean = Class.forName(beanName).newInstance();
            invokeMethod(bean, methodName, methodParams);
        }
    }

    /**
     * 調(diào)用任務(wù)方法
     *
     * @param bean         目標(biāo)對(duì)象
     * @param methodName   方法名稱
     * @param methodParams 方法參數(shù)
     */

    private static void invokeMethod(Object bean, String methodName, List<Object[]> methodParams)
            throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException,
            InvocationTargetException 
{
        if (StringUtils.isNotNull(methodParams) && methodParams.size() > 0) {
            Method method = bean.getClass().getDeclaredMethod(methodName, getMethodParamsType(methodParams));
            method.invoke(bean, getMethodParamsValue(methodParams));
        } else {
            Method method = bean.getClass().getDeclaredMethod(methodName);
            method.invoke(bean);
        }
    }

    /**
     * 校驗(yàn)是否為為class包名
     *
     * @param invokeTarget 名稱
     * @return true是 false否
     */

    public static boolean isValidClassName(String invokeTarget) {
        return StringUtils.countMatches(invokeTarget, ".") > 1;
    }

    /**
     * 獲取bean名稱
     *
     * @param invokeTarget 目標(biāo)字符串
     * @return bean名稱
     */

    public static String getBeanName(String invokeTarget) {
        String beanName = StringUtils.substringBefore(invokeTarget, "(");
        return StringUtils.substringBeforeLast(beanName, ".");
    }

    /**
     * 獲取bean方法
     *
     * @param invokeTarget 目標(biāo)字符串
     * @return method方法
     */

    public static String getMethodName(String invokeTarget) {
        String methodName = StringUtils.substringBefore(invokeTarget, "(");
        return StringUtils.substringAfterLast(methodName, ".");
    }

    /**
     * 獲取method方法參數(shù)相關(guān)列表
     *
     * @param invokeTarget 目標(biāo)字符串
     * @return method方法相關(guān)參數(shù)列表
     */

    public static List<Object[]> getMethodParams(String invokeTarget) {
        String methodStr = StringUtils.substringBetween(invokeTarget, "("")");
        if (StringUtils.isEmpty(methodStr)) {
            return null;
        }
        String[] methodParams = methodStr.split(",");
        List<Object[]> classs = new LinkedList<>();
        for (int i = 0; i < methodParams.length; i++) {
            String str = StringUtils.trimToEmpty(methodParams[i]);
            // String字符串類型,包含'
            if (StringUtils.contains(str, "'")) {
                classs.add(new Object[]{StringUtils.replace(str, "'"""), String.class});
            }
            // boolean布爾類型,等于true或者false
            else if (StringUtils.equals(str, "true") || StringUtils.equalsIgnoreCase(str, "false")) {
                classs.add(new Object[]{Boolean.valueOf(str), Boolean.class});
            }
            // long長(zhǎng)整形,包含L
            else if (StringUtils.containsIgnoreCase(str, "L")) {
                classs.add(new Object[]{Long.valueOf(StringUtils.replaceIgnoreCase(str, "L""")), Long.class});
            }
            // double浮點(diǎn)類型,包含D
            else if (StringUtils.containsIgnoreCase(str, "D")) {
                classs.add(new Object[]{Double.valueOf(StringUtils.replaceIgnoreCase(str, "D""")), Double.class});
            }
            // 其他類型歸類為整形
            else {
                classs.add(new Object[]{Integer.valueOf(str), Integer.class});
            }
        }
        return classs;
    }

    /**
     * 獲取參數(shù)類型
     *
     * @param methodParams 參數(shù)相關(guān)列表
     * @return 參數(shù)類型列表
     */

    public static Class<?>[] getMethodParamsType(List<Object[]> methodParams) {
        Class<?>[] classs = new Class<?>[methodParams.size()];
        int index = 0;
        for (Object[] os : methodParams) {
            classs[index] = (Class<?>) os[1];
            index++;
        }
        return classs;
    }

    /**
     * 獲取參數(shù)值
     *
     * @param methodParams 參數(shù)相關(guān)列表
     * @return 參數(shù)值列表
     */

    public static Object[] getMethodParamsValue(List<Object[]> methodParams) {
        Object[] classs = new Object[methodParams.size()];
        int index = 0;
        for (Object[] os : methodParams) {
            classs[index] = (Object) os[0];
            index++;
        }
        return classs;
    }
}

啟動(dòng)程序后可以看到,調(diào)度器已經(jīng)啟動(dòng):

2021-10-06 16:26:05.162  INFO 10764 --- [shivaScheduler]] o.s.s.quartz.SchedulerFactoryBean        : Starting Quartz Scheduler now, after delay of 1 seconds
2021-10-06 16:26:05.306  INFO 10764 --- [shivaScheduler]] org.quartz.core.QuartzScheduler          : Scheduler shivaScheduler_$_DESKTOP-OKMJ1351633508761366 started.

13新增調(diào)度任務(wù)

添加任務(wù),使用如下 json 進(jìn)行請(qǐng)求:

{
  "concurrent""1",
  "cronExpression""0/10 * * * * ?",
  "invokeTarget""mysqlJob.execute('got it!!!')",
  "jobGroup""mysqlGroup",
  "jobId"9,
  "jobName""新增 mysqlJob 任務(wù)",
  "misfirePolicy""1",
  "remark""",
  "status""0"
}
@Override
@Transactional(rollbackFor = Exception.class)
public int insertJob(QuartzJob jobthrows Exception 
{
    // 先將任務(wù)設(shè)置為暫停狀態(tài)
    job.setStatus(ScheduleConstants.Status.PAUSE.getValue());
    int rows = quartzMapper.insert(job);
    if (rows > 0) {
        ScheduleUtils.createScheduleJob(scheduler, job);
    }
    return rows;
}

先將任務(wù)設(shè)置為暫停狀態(tài),數(shù)據(jù)庫(kù)插入成功后,在調(diào)度器創(chuàng)建任務(wù)。

再手動(dòng)啟動(dòng)任務(wù),根據(jù) ID 來(lái)啟動(dòng)任務(wù):

實(shí)現(xiàn)代碼:

@Override
    public int changeStatus(Long jobId, String status) throws SchedulerException {
        int rows = quartzMapper.changeStatus(jobId, status);
        if (rows == 0) {
            return rows;
        }
        //更新成功,需要改調(diào)度器內(nèi)任務(wù)的狀態(tài)
        //拿到整個(gè)任務(wù)
        QuartzJob job = quartzMapper.selectJobById(jobId);
        //根據(jù)狀態(tài)來(lái)啟動(dòng)或者關(guān)閉
        if (ScheduleConstants.Status.NORMAL.getValue().equals(status)) {
            rows = resumeJob(job);
        } else if (ScheduleConstants.Status.PAUSE.getValue().equals(status)) {
            rows = pauseJob(job);
        }
        return rows;
    }
@Override
public int resumeJob(QuartzJob job) throws SchedulerException {
    Long jobId = job.getJobId();
    String jobGroup = job.getJobGroup();
    job.setStatus(ScheduleConstants.Status.NORMAL.getValue());
    int rows = quartzMapper.updateById(job);
    if (rows > 0) {
        scheduler.resumeJob(ScheduleUtils.getJobKey(jobId, jobGroup));
    }
    return rows;
}

暫停任務(wù)的代碼也相同。

調(diào)用啟動(dòng)后可以看到控制臺(tái)打印日志:

2021-10-06 20:36:30.018  INFO 8536 --- [eduler_Worker-3] cn.shiva.quartz.job.MysqlJob             : 執(zhí)行 Mysql Job,當(dāng)前時(shí)間:2021-10-06 20:36:30,任務(wù)參數(shù):got it!!!
2021-10-06 20:36:40.016  INFO 8536 --- [eduler_Worker-4] cn.shiva.quartz.job.MysqlJob             : 執(zhí)行 Mysql Job,當(dāng)前時(shí)間:2021-10-06 20:36:40,任務(wù)參數(shù):got it!!!
2021-10-06 20:36:50.017  INFO 8536 --- [eduler_Worker-5] cn.shiva.quartz.job.MysqlJob             : 執(zhí)行 Mysql Job,當(dāng)前時(shí)間:2021-10-06 20:36:50,任務(wù)參數(shù):got it!!!

如果涉及到任務(wù)修改,需要在調(diào)度器先刪除原有任務(wù),重新創(chuàng)建調(diào)度任務(wù)。

啟動(dòng)初始化任務(wù)

這部分倒是比較簡(jiǎn)單,初始化的時(shí)候清空原有任務(wù),重新創(chuàng)建就好了:

/**
  * 項(xiàng)目啟動(dòng)時(shí),初始化定時(shí)器 主要是防止手動(dòng)修改數(shù)據(jù)庫(kù)導(dǎo)致未同步到定時(shí)任務(wù)處理(注:不能手動(dòng)修改數(shù)據(jù)庫(kù)ID和任務(wù)組名,否則會(huì)導(dǎo)致臟數(shù)據(jù))
  */

@PostConstruct
public void init() throws Exception {
    scheduler.clear();
    List<QuartzJob> jobList = quartzMapper.selectJobAll();
    for (QuartzJob job : jobList) {
        ScheduleUtils.createScheduleJob(scheduler, job);
    }
}

14其他說明

并發(fā)執(zhí)行

上面有并發(fā)和非并發(fā)的區(qū)別,通過 @DisallowConcurrentExecution 注解來(lái)實(shí)現(xiàn)阻止并發(fā)。

Quartz定時(shí)任務(wù)默認(rèn)都是并發(fā)執(zhí)行的,不會(huì)等待上一次任務(wù)執(zhí)行完畢,只要間隔時(shí)間到就會(huì)執(zhí)行, 如果定時(shí)任執(zhí)行太長(zhǎng),會(huì)長(zhǎng)時(shí)間占用資源,導(dǎo)致其它任務(wù)堵塞。

@DisallowConcurrentExecution 禁止并發(fā)執(zhí)行多個(gè)相同定義的JobDetail, 這個(gè)注解是加在Job類上的, 但意思并不是不能同時(shí)執(zhí)行多個(gè)Job, 而是不能并發(fā)執(zhí)行同一個(gè)Job Definition(由JobDetail定義), 但是可以同時(shí)執(zhí)行多個(gè)不同的JobDetail。

舉例說明,我們有一個(gè)Job類,叫做SayHelloJob, 并在這個(gè)Job上加了這個(gè)注解, 然后在這個(gè)Job上定義了很多個(gè)JobDetail, 如sayHelloToJoeJobDetail, sayHelloToMikeJobDetail, 那么當(dāng)scheduler啟動(dòng)時(shí), 不會(huì)并發(fā)執(zhí)行多個(gè)sayHelloToJoeJobDetail或者sayHelloToMikeJobDetail, 但可以同時(shí)執(zhí)行sayHelloToJoeJobDetailsayHelloToMikeJobDetail

@PersistJobDataAfterExecution 同樣, 也是加在Job上。表示當(dāng)正常執(zhí)行完Job后, JobDataMap中的數(shù)據(jù)應(yīng)該被改動(dòng), 以被下一次調(diào)用時(shí)用。

當(dāng)使用 @PersistJobDataAfterExecution 注解時(shí), 為了避免并發(fā)時(shí), 存儲(chǔ)數(shù)據(jù)造成混亂, 強(qiáng)烈建議把 @DisallowConcurrentExecution 注解也加上。

測(cè)試代碼,設(shè)定的時(shí)間間隔為3秒,但job執(zhí)行時(shí)間是5秒,設(shè)置 @DisallowConcurrentExecution以 后程序會(huì)等任務(wù)執(zhí)行完畢以后再去執(zhí)行,否則會(huì)在3秒時(shí)再啟用新的線程執(zhí)行。

阻止特定時(shí)間運(yùn)行

仍然是通過調(diào)度器實(shí)現(xiàn)的:

//2014-8-15這一天不執(zhí)行任何任務(wù)
Calendar c = new GregorianCalendar(2014715);
cal.setDayExcluded(c, true);
scheduler.addCalendar("exclude", cal, falsefalse);

//...中間省略
TriggerBuilder.newTrigger().modifiedByCalendar("exclude")....

來(lái)源:https://blog.csdn.net/m0_46144826


-End-

最近有一些小伙伴,讓我?guī)兔φ乙恍?nbsp;面試題 資料,于是我翻遍了收藏的 5T 資料后,匯總整理出來(lái),可以說是程序員面試必備!所有資料都整理到網(wǎng)盤了,歡迎下載!

點(diǎn)擊??卡片,關(guān)注后回復(fù)【面試題】即可獲取

在看點(diǎn)這里好文分享給更多人↓↓

瀏覽 76
點(diǎn)贊
評(píng)論
收藏
分享

手機(jī)掃一掃分享

分享
舉報(bào)
評(píng)論
圖片
表情
推薦
點(diǎn)贊
評(píng)論
收藏
分享

手機(jī)掃一掃分享

分享
舉報(bào)

感谢您访问我们的网站,您可能还对以下资源感兴趣:

国产秋霞理论久久久电影-婷婷色九月综合激情丁香-欧美在线观看乱妇视频-精品国avA久久久久久久-国产乱码精品一区二区三区亚洲人-欧美熟妇一区二区三区蜜桃视频 日本特级黄A片免费观看| www操逼| 一区二区三区免费在线观看| 一边做一边说国语对白| 日本三级黄色视频| 欧美成人一区二区三区| 白嫩外女BBWBBWBBW| 一级片学生妹| 日韩超清无码| 爆菊花综合网| 国产黄色在线观看| 日韩欧美色图| 欧美黑人操逼| 欧美激情片| www.re99| 精品人妻无码一区二区三区| 久久久精品电影91| 波多野结衣AV无码| www.yw尤物| 乱子伦国产精品一区二区| 中文字幕av免费在线观看| 人人肏人人射| 日韩免费福利视频| 日韩无码久久| 无码精品人妻一区二区三刘亦菲| 丁香五月婷婷六月| 亚洲男人天堂视频| 成人免费网站在线观看| 高清无码视频在线| 欧美日韩中字| 无码三级在线免费观看| 欧美黄片在线| 三级黄视频| 你懂得在线观看| 91久久精品无码一区| AV资源在线免费观看| 西西444WWW无码大胆在线观看| 国产Av婬乱麻豆| 夜夜国自一区| 欧美综合国产| 亚洲天堂综合网| 国产精品无码ThePorn| 97久久精品| 国产视频97| 日区无码| AV天堂国产| 亚洲视频免费| 最好看的MV中文字幕国语电影 | 操比在线| 高清无码日本| av在线一区二区三区| 三级视频国产| 亚洲波多野结衣| 人妻av一区二区三区| 骚逼视频聊天记录| 日韩一级二级| 日韩免费一级片| 真实国产乱子伦毛片| 成人啪啪网站| 久久成人电影院| 成人中文字幕在线观看| 西西人体视频| www.欧美视频| 免费日B视频| 国产精品伦子伦免费视频| 午夜AV免费| www.在线播放| 国产黄h| 亚洲九九| 婷婷久久综合| 国产成人精品AV在线观| 日韩成人视频在线观看| 国产精品久久久久毛片SUV| 99久久久久久| 日本色色网站| 91久久香蕉囯产熟女线看蜜桃| 天天撸一撸视频| 亚洲成人视频免费在线观看 | 日本一区二区在线视频| 高潮喷水无码| 国产主播AV| 91av导航| 高清无码视频在线| 日韩欧美a片| av无码导航| 亚洲无码A片在线| 国产精品伦理| 色欧美亚洲| 麻豆免费版在线观看| 99视频在线| 欧美三级在线播放| 天天干天天干天天| 欧美亚洲天堂| 操一操| 亚欧无码| 国产主播在线观看| 高清无码免费| 欧美日韩免费在线播放电影在线播放电影在线播放电影免费 | 玩弄人妻少妇500系列视频 | 内射视频免费观看| 久久亚洲免费视频| 欧美少妇视频| 日本高清无码视频| 99热在线观看精品免费| 四lll少妇BBBB槡BBBB| 成人久久网| 久久国内| 欧美成人第一页| 亚洲欧洲AV| 在线免费黄| 91久久香蕉囯产熟女线看蜜桃| 婷婷五月天网址| 操逼a片| 综合国产| 91中文字幕+乱码| 亚洲有码在线| 免费毛片+一区二区三区| 亚洲天堂2015| 国产亚洲欧美在线| 亚洲中文字幕网| 8050午夜网| AV在线免费播放| 一级日韩| 日本高清一区二区高清免费视频| 欧美日本成人网站入口| 日韩av中文在线| 黄片高清免费观看| 婷婷五月天在线电影| 亚洲日本一区二区三区| 久久国产日韩| 秋霞午夜福利影院| 在线黄色视频网站| 人人人人人人人人操| 大香蕉网伊人在线| 午夜久久电影| 新妺妺窝窝777777野外| 欧美群交在线观看| 亚洲色情在线观看| 精品人妻一区二区三区-国产精品| 成人免费无码| 国产又爽又黄免费网站免费观看| 天天干天天摸| 日韩乱伦毛片| 五月天在线电影| 男人的天堂色琪琪| 婷婷情色| 蜜臀久久99精品久久久| 51妺嘿嘿午夜福利在线| 国产色视频一区二区三区QQ号| 无码人妻精品一区二区蜜桃91| 国产精品久久久久久亚洲影视| 99久久精品国产精品有折扣吗| 久久夜色精品国产欧美乱极品 | 久在线观看| 日韩成人无码AV| 国产在线一区二区| 欧美日韩免费在线播放电影在线播放电影在线播放电影免费 | 亚洲A片一区二区三区电影网| 亚洲人成人无码一区二区三区| 亚洲AV免费电影| 日本操b| 91日逼视频| 国产无码激情视频| 久久青青| 十八禁网站在线| 操逼操逼操| 99久久人妻无码中文字幕系列| 黄色日逼网站| 伊人国产视频| 成人毛片在线播放| 色综合久久88色综合天天| 亚洲色图15P| 日韩三级片av| 欧洲无码一区二区三区| 免费观看一区| 99久久夜色精品国产亚洲| 成年人黄色电影| 亚洲免费一区二区| 亚洲第一在线| 撸撸操在线视频观看只有精品 | 亚洲黄色录像| 欧美性爱一区| 成人社区视频| 亚洲精品日韩综合观看成人91 | 色操逼网| 大香蕉色伊人| 91网站免费观看| 亚洲无码在线免费观看视频| 在线看片a| 亚洲A网站| 亚洲AV无码成人精品区h麻豆| 国产AV中文字幕| 大香蕉超碰| 一区二区三区国产视频| 99视频自拍| 欧美操逼在线观看| 影音先锋av资源在线| 日韩成人一区二区三区| 五月丁香网站| 国产欧美毛片| 久久av影院| 日本成人黄色电影| 国产成人午夜福利在线| 91视频网址| 免费一级黄色毛片| 午夜试看120秒体验区的特点| 亚洲成人性爱视频| 伊人网大香| 北条麻妃中文字幕在线| 午夜成人小视频| 青青操青青干| 人人看人人摸人人草| 少妇bbw搡bbbb搡bbbb | 亚洲午夜无码精品专区| 精品人妻一区二区三区蜜桃| 亚洲天堂高清| 翔田千里AV在线| 欧洲精品码一区二区三区免费看| 中文字幕无码A片久久| 在线观看精品视频| 又黄又爽无遮挡| 日韩无码小电影| 激情性爱婷婷色五月| 亚洲福利网| 成人AV一区二区三区| 免费看一区二区三区| 亚洲欧美性爱| 久久综合站| 日韩一区二区三区无码| 成人做爰100片免费观看视频| 欧美成人精品AAA| 中文字幕码精品视频网站| 日本在线观看www| 欧美在线日韩| 91三级| 韩国GOGOGO高清| 亚洲操屄| 奇米97| 中文字幕人妻无码| 国产麻豆电影在线观看| 美女人人操| 91在线综合| 不卡二区| 国产成人高清在线| 东京热久久综合| 三级在线观看视频| 中文字幕一区二区无码成人| 日本AⅤ中文字幕| 九九精品在线视频| 久操中文| 无码一区二区三区四季| 天天干免费视频| 欧美肉大捧一进一出小说| 91久色| 天天操夜夜操视频免费高清| 婷婷看片| 亚洲黄色免费在线观看| 色色免费黄色视频| 国产精品v欧美精品v日韩| 丁香五月天啪啪| 一区二区三区无码视频| 欧美成人在线免费| 婷婷国产在线| 久久青| 一道本不卡视频| 蜜桃成人无码区免费视频网站| 在线观看黄色AV| 欧美五月在线网址| 在线中文字幕亚洲| 青青自拍视频| 国产在线不卡年轻点的| 狠狠色五月| 高清无码激情| 九九九精品| 国产又爽又黄视频| 无码AV中文字幕| 日韩一级黄色电影| 一本之道DVD不卡视频| 国产男女啪啪视频| 97无码| 先锋AV资源站| 怮交小拗女小嫩苞视频| 99偷拍| 亚洲无码在线精品| 黄色一级在线观看| 爆乳一区二区三区AV| 国产一区二区不卡亚洲涩情| 日本免费高清视频| 欧美3P视频| 激情午夜av| 99激情视频| 特猛特黄AAAAAA片| 成人做爰黄A片免费视频网站野外 国产成人午夜精品无码区久久麻豆 | zzjicom| 十八禁无码| 四虎精品一区二区三区| 久久国产精彩视频| 热99视频| 中文字幕在线观看辣文| 激情国产AV| 亚洲无码精品在线| 亚洲精品天堂无码| 超碰操| 欧美在线视频99| 高潮喷水无码| 激情五月天色| A片在线免费播放| 无码专区中文字幕| 亚洲在线看| 五月天激情av| 免费v片在线观看| 欧美激情DVD| 大香蕉大香蕉网| 久草综合视频| 国产精品伦子伦免费视频| 成人大香蕉网| 中文字幕高清无码免费视频| 成年人国产| 亚洲日本在线观看| 特级西西444www大胆高清图片| 六月丁香视频| 天天干强奸视频在线综合| 亚洲无码资源| 狠狠的日| 中文字幕日本| 精品91海角乱| WWW亚洲视频| 色色777| 久久久999精品视频| 99热国产在线观看| 欧美性爱视频网站| 日韩成人大片| 成人欧美在线| PORNY九色视频9l自拍| 一级黄色片在线观看| 超碰在线网| 日日干综合| 国产乱伦不卡| 五月婷婷色色色| 日韩黄色网| 久久国产精品99久久人人澡| 麻豆91在线| 日韩少妇AV| 香蕉操逼视频| 一道本激情视频| 久肏| 99er这里只有精品| 拍真实国产伦偷精品| 不卡一二三区| 韩国精品在线观看| 韩国毛片基地久久| 欧美a片在线| 中文字幕永久在线| 免费成人一级片| 五月天激情网站| 影音先锋av在线资源| 蜜臀久久99精品久久久老牛影视| 免费在线观看亚洲| 一级黄色视频网站| 人人弄| 一区二区毛片| 黄色一级小说| 日本不卡一区二区三区| 天天操人人操| 日本精品一区二区三区四区的功能| 亚洲日韩精品成人无码专区AV| 久久77| 国产波霸爆乳一区二区| 日韩中文字幕久久| 91亚瑟视频| 亚洲无码A片在线观看| 精品久久无码中文字幕| www.污| 香蕉在线播放| 91资源在线| 亚洲高清视频在线播放| 国产成人99久久亚洲综合精品| 亚洲高清视频在线播放| 大香蕉操逼视频| 影音先锋色AV| 久热精品视频| 国产香蕉在线视频| 三级片国产| 女生操逼网站| 日韩三级在线免费观看| 国产三级午夜理伦三级| 91福利资源| 操逼观看| 五月天激情av| 免费看黄色大片| 黄色福利视频在线观看| wwwA片| 手机av在线观看| 欧美级毛片高潮| 欧美亚洲日韩成人| 天堂无码视频在线播放| 免费视频91蜜桃| 无码人妻丰满熟妇区17水蜜桃 | aaa在线免费视频| 一级一A片一a免费看| 白丝久久| 草久视频| 二区视频在线| 无码在线免费观看视频| chinese搡老熟老妇人| 婷婷色色婷婷五月天| 中文字幕亚洲欧美| 德国肥妇熟妇BBwBBw| 国产欧美日韩| 午夜久久福利| 91高清视频| 91香蕉国产视频| 最新午夜综合福利视频| 黄片无码免费观看| 欧美色图狠狠操| 国产在线观看一区二区| 一级AV在线| 91在线观看高清18| 亚洲性片| 色多多导航| 女人久久久| 在线中文字幕亚洲| 人妻无码视频| 免费色网站| 日韩无码一卡二卡| 国产精品欧美7777777| 无码六区| 免费看欧美日黄片| 青草综合| 黄色小视频免费| 四川少妇搡bbbbb搡多人| 三级网站视频| 91熟女首页| 成人国产精品视频| 免费观看一区| 亚洲秘无码一区二区三区胖子| 九九国产| 国产熟妇搡BBBB搡BBBB搡| 国产一区二区在线视频| 91艹逼| 欧美成人性爱网址| 国产一级片在线播放| 免费一区视频| 91大铭哥| 一级全黄120分钟免费| 亚洲黄色在线免费观看| 免费观看毛片| 九九国产| 性爱福利导航| 水蜜桃一区二区| 日韩无码字幕| 北条麻妃无码视频在线| 欧美亚洲一区| 欧美肥臀| 日韩激情视频在线观看| 日韩一级一级| 欧美第一色| 国产午夜福利免费视频在线观看| 国产熟妇码视频| AV免费网址| 人人操人人操人人操人人操人人操 | 爱搞视频在线观看| 久草国产视频| 大香蕉中文视频| 国产麻豆| 日韩中文一区| 欧美亚洲国产一区二区三区| 成人国产精品秘在线看| 91在线| 国产亚洲无码激情| 亚洲精品乱码久久久久久按摩观| 特级黄色视频| 日本视频免费| 色综合五月| 亚洲日本欧美| 肏屄一区| 精品国产免费无码久久噜噜噜AV| 亚洲日韩中文字幕在线观看| 亚洲无码色| 日韩1234区| 操逼视频免费看| 九九九中文字幕| AV第一福利大全导航| 国产精品秘入口18禁网站| 中文字幕有码在线视频| 日韩视频久久| 国产一级特黄| 美女被操网站| 日韩激情AV| 日韩欧美人妻无码精品| 日韩一级一片内射视频4K| 蜜桃秘av一区二区三区安全| 国产又粗又猛又爽又黄91精品 | 激情综合在线| 青青草黄色片| 毛片一区二区三区| 天天日天天搞| 在线播放高清无码| 鲁一鲁视频| 毛片网站在线| 国产剧情91| 成人A片免费在线观看| 免费成人黄视频| 果冻传媒一区二区三区| 亚洲小穴| 成人自拍网站| 久久成人123| 内射无码专区久久亚洲| 国产在线观看91| 婷婷电影网| 高清无码视频在线观看| 日韩免费在线观看视频| 国产在线观看不卡| 97色情| 无码三级在线免费观看| 人人妻人人爽人人操| 俺去也在线视频| 国产精品中文字幕在线观看| 正在播放亚洲| 日韩AV毛片| 亚洲午夜精品视频| 国产中文字幕第一页| 加勒比日韩无码| 五月丁香五月婷婷| 逼特逼视频在线观看| 九一成人电影| 国产精品九九视频| 亚洲无码视频在线观看| 亚洲人妻少妇| 精品黄色片| A片黄色视频| 最新中文字幕在线播放| 免费黄色AV| 91狠狠色丁香婷婷综合久久| 夜夜撸天天干| 影音先锋蜜桃| 亚洲婷婷丁香| 成人精品在线观看| 91好爽| 西西人体44www大胆无码| 97免费在线观看视频| 久久久999精品视频| 中文无码在线播放| 无码在线免费视频| 午夜精品人妻无码| 无码国产av| 懂色一区二区二区在线播放视频| 国产精品国产伦子伦露看| 久久99老妇伦国产熟女| 国产第一页在线播放| www.xxx国产| 色五月视频在线| 国产三级在线| 久久视频免费看| 亚洲无码在线免费| www.18av| 少妇厨房愉情理伦BD在线观| 欧美,日韩,中文字幕| 日本A视频| 午夜看片| 日韩激情网站| 在线观看国产小视频| 国产女人与禽zOz0性| 亚洲AV无码成人精品区东京热 | 在线免费高清无码| 色欲影视插综合一区二区三区 | 97人人爽人人爽人人人| 亚洲秘一区二区三区-精品亚洲二区- | 成人a电影| 中文字幕高清无码免费视频| 免费黄色在线| AV无码免费观看| 自拍乱伦| 国产成人AV在线观看| 一区二区三区www污污污网站| 国产精品欧美激情| 日韩免费在线观看一区入口| 岛国AV在线| 国产精品秘麻豆果冻传媒潘甜甜丶| 伊人综合网站| 91蜜桃在线观看| 丁香六月久久| 欧美日韩一级二级三级| 国产一级a毛片| 日本高清视频网站网wwwwww| 成人在线观看网站| 色久在线| 99热在线观看精品免费| 五月丁香激情综合| 偷拍亚洲色图| 色综合天| 日日骚亚洲| 乱子伦一区二区三区视频在线观看 | 成人中文字幕在线视频| 天堂vs亚洲| 免费18蜜桃久久19| 人妻第一页| 黄色A片网站| 欧美a在线观看| 狠狠干中文字幕| 日日骚av一区二区三区| 少妇无码在线| 韩国gogogo高清在线完整版| 爽爽午国产浪潮AV性色www| 国产欧美二区综合中文字幕精品一 | 成人在线视频播放| 国产精品热| 亚洲高清无码在线免费观看| 91人人操人人爽| 午夜黄色视频在线观看| av不卡在线观看| 国产福利在线导航| 国产一级A片免费看| 伊人操逼网| 中文字幕免费在线看一区七区 | 俺也来俺也去WWW色| www.怡春院| 成人做爰免费网站2023| 国产综合自拍| 久热超碰| 免费a在线观看| 国产欧美精品一区二区色综合| 日韩无码久久| 久久久无码精品亚洲日韩男男| 奥门毛片| 青娱乐网站| 97久久精品| 成人欧美视频| 韩国精品久久久| 麻豆传媒免费观看| 亚洲AV免费看| 国产黄色一级片| 四季AV一区二区凹凸懂色桃花| 在线观看黄色网页| 久一精品| 一区二区免费| 又粗又硬又爽18级A片| 亚洲AV永久无码国产精品久久| jlzz18| 婷婷国产综合| 97人人干人人| 99re在线视频观看| 成人一级精品| 91亚洲国产成人精品一区二区三 | 444444在线观看免费高清电视剧木瓜一 | 超碰777| 国精产品一区一区三区四区| 大香蕉99热| 在线观看国产小视频| 国产成人精品电影| 久久久成人网| 五月丁香视频在线| 操逼视频高清无码| 亚洲日韩成人电影| 久久99九九| 欧美操日本| 青草久久视频| 深爱婷婷| 一道本激情视频| 亚州成人视频| 操操影视| 熟女视频一区二区| 超碰观看| 日韩a片| 91女人18片女毛片60分钟| 日韩人妻精品无码| 亚洲AV永久无码精品| 中文在线观看视频| 人妻av在线| 日本黄色三级片| 亚洲最大黄色| AV无码在线观看| 蜜臀久久99精品久久久久久牛牛| 中国老太卖婬HD播放| 韩日黄色| 老熟女AV| 免费观看日韩无码视频| 日欧一级片| 色男人的天堂网| 无码三级av| 欧美在线| 69av在线观看视频| 亚洲天堂视频在线观看免费| 在线中文字幕AV| 97人妻精品一区二区三区视频 | 人妻无码在线观看| 无码国产精品一区二区性色AV| 91探花秘入囗| 日本精品视频一区二区| 亚洲日韩在线中文字幕| 精品乱子伦一区二区三区在线播放 | 成人久久网| 91人人妻人人澡| 亚洲天堂2015| 天堂中文字幕在线观看| 最新中文字幕在线观看| 精品无码一区二区三区四区久久久软件 | 亚洲日韩视频在线播放| 另类老太婆性BBWBBw| 草莓视频在线播放| 91av成人| 精品无码人妻| 性感91影院| 黄色大片av| 亚洲AV资源在线| 狠狠艹狠狠干| 羞羞午夜| XXXXⅩHD亚洲人HD| 久久国产片| 性猛交AAAA片免费看蜜桃视频| 天天插天天干| 欧美操操操| 国产一区二区免费看| 午夜成人黄色电影| 欧洲a视频| 国产久久久久久久久久| 天天摸天天干| 欧美一区二区在线视频| 日本性爱一区| 天天天天天天干| 人人爱人人操| 国产国产国产在线无码视频| 中文在线第一页| 成人A片在线| 欧美大骚逼| AV在线一区二区| 精品成人电影| 免费v片| 国产做受精品网站在线观看| 日韩成人无码影片| 欧美亚洲视频在线观看| 成人免费黄色片| 啊啊啊啊啊网站| 99久久久久久久| 黄色视频网站免费| 中出欧美亚洲| 91丨九色丨蝌蚪丨对白| 日本无码视频在线| 日韩中文字幕精品| eeuss国产| 国产免费成人视频| 东京热免费视频| xxx久久| 双腿张开被9个男人调教| 91逼站| www.狠狠爱| 亚洲国产精品久久人人爱| a√天堂中文8| 久草黄色电影| A级片免费看| 天天艹天天干| 免费看国产黄色视频| 性爱视频免费网站| 肏逼网| 一级黄色av| 中文字幕+乱码+中文乱码视频在线观看 | 三级片在线看片AV| 成人性生交片无码免费看人| www.五月天.con| 99热在线观看免费精品| 草逼小视频| 熟妇无码| 国产精品aaa| 91成人视频免费观看| 一本一本久久a久久精品牛牛影视| 日本黄色视| 中文字幕在线免费视频| 久久久久久97电影院电影院无码| 国产精品色哟哟| 婷婷午夜精品久久久久久性色| 日本特黄AA片免费视频| 亚洲毛片在线观看| 天天影视综合网免费观看电视剧国产| 伊人av网| 最近日本中文字幕中文翻译歌词| 男女日逼网站| 一级A片亲子乱| 无码不卡视频在线观看| 另类综合激情| 婷婷国产精品视频| 欧美不卡一区二区三区| 91无码精品一区二区| 久久久亚洲AV| 国产欧美日韩综合在线视频| 视频一区二区三| 午夜AV影院| 中文字幕免费| 婷婷精品秘进入| 加勒比人妻| 国内精品久久久久| 99精品久久| 97无码人妻一区二区三区| 理论在线视频| 日韩小黄片| 欧美日韩卡一卡二在线播放视频| 无码人妻丰满熟妇精品区| 日韩精品毛片| 玖玖99视频| 全国最大成人网站| 婷婷激情中文字幕| 免费在线亚洲| 91秦先生在线播放| 欧美肏逼视频| 国产51视频| 18禁一区二区| 久久aa| 欧美视频在线观看| 久久九九视频| 爱逼综合| 中文字幕在线有码| 狼友视频一国产| 日本成人免费| 69er小视频| 乱伦性爱视频| 人人干人人操人人爱| 国产在线看片| 国产色综合视频| 大香蕉av在线| 一级A片黃色A片| 青青在线免费视频| 91成人视频在线播放| 2019中文字幕在线| 狠狠色五月| 日韩一卡二卡| 一区精品| 有免费的欧美操逼视频吗| 无码人妻一区二区三区免水牛视频| 亚洲,制服,综合,中文| 骚妇p| 无码一区二区黑人猛烈视频网站| 国产三级午夜理伦三级| 天天干天天干天天| 免费看黄视频| 9999国产精品| 三级片在线观看视频| 午夜AV福利影院| 五月丁香激情婷婷| 蝌蚪窝在线观看| 久久这里有精品视频| 就要操逼| 成人爽a毛片一区二区免费| 欧美综合亚洲图片综合区| 18禁看网站| 深爱五月网| 黑人操逼| 国产精品国产三级国产AⅤ| 日韩午夜av| 亚洲av影院| 午夜精品久久久久久久99热精东 | 波多野结衣亚洲视频| 亚洲精品午夜福利| 欧美日韩亚洲一区二区三区 | 99色热| 亚洲日韩免费在线观看| 成年人黄色视频网站| h片免费观看| 国产精品熟女| 99久re热视频精品98| 免费高清无码在线观看| 国产美女操逼网站| 黄色一级片免费观看| 日韩无码一二三| 吴梦梦| 午夜神马影院| 国产内射在线观看| 色色网站视频| 69国产在线| 亚洲操| 亚洲成人无码高清| 日韩黄片免费看| 狠狠干狠狠操| 在线高清无码视频| 黄片在线免费观看视频| 狼友视频第二页| 日韩三级在线播放| 亚洲三级在线播放| 国产骚逼视频| 要操逼网| 成人免费无码婬片在线| jizz免费观看| 久久黄色免费视频| 久操视频在线| 色视频免费在线观看| 老司机AV| 俺也去俺去啦| 特级欧美AAAAAA| 一区二区三区视频免费| 成人免费av| 四lll少妇BBBB槡BBBB| 亚洲国产精品成人综合色五月| 欧美A级成人婬片免费看| 亚洲操b| 亚洲中文视频在线| 手机在线看A片| 蜜桃视频一区二区三区| 日韩无码AV中文字幕| 草久在线视频| 玖玖热在线视频| 国产在线视频91| 日韩人妻丰满无码区A片|