quartz的初步總結及配置優化


1.scheduler

1. Scheduler就是Quartz的大腦,所有任務都是由它來設施。
Scheduler包含一個兩個重要組件: JobStore和ThreadPool。
JobStore是會來存儲運行時信息的,包括Job、JobDetail、Trigger以及業務鎖等。它有多種實現RAMJob(內存實現),JobStoreTX(JDBC,事務由Quartz管理),JobStoreCMT(JDBC,使用容器事務),ClusteredJobStore(集群實現)。

ThreadPool就是線程池,Quartz有自己的線程池實現。所有任務的都會由線程池執行。

2.SchdulerFactory,顧名思義就是來用創建Schduler了,有兩個實現:DirectSchedulerFactory和 StdSchdulerFactory。前者可以用來在代碼里定制你自己的Schduler參數。后者是直接讀取classpath下的quartz.properties(不存在就都使用默認值)配置來實例化Schduler。通常來講,我們使用StdSchdulerFactory也就足夠了。

org.quartz.scheduler.instanceName = DefaultQuartzScheduler
org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool
org.quartz.threadPool.threadCount = 10 
org.quartz.threadPool.threadPriority = 5
org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread = true
org.quartz.jobStore.class = org.quartz.simpl.RAMJobStore

  

2.Trigger

2.1 simpleTrigger

SimpleTrigger可以滿足的調度需求是:在具體的時間點執行一次,或者在具體的時間點執行,並且以指定的間隔重復執行若干次。比如,你有一個trigger,你可以設置它在2018年12月9日的上午11:23:54准時觸發,或者在這個時間點觸發,並且每隔2秒觸發一次,一共重復5次。

public static void main(String[] args) throws SchedulerException {
        // 創建Scheduler
        SchedulerFactory sf = new StdSchedulerFactory();
        Scheduler scheduler = sf.getScheduler();
        // 需求:我要在5秒之后執行任務,時間間隔為2秒,最多執行100次
        long currentTime = System.currentTimeMillis();
        long delayTime = currentTime + 5 * 1000l; // 5秒之后執行任務
        // 設置Trigger(不再使用靜態方法)
        Trigger trigger = TriggerBuilder.newTrigger() // 使用TriggerBuilder創建Trigger
                .withIdentity("trigger1", "group1")
                .startAt(new Date(delayTime))
                .withSchedule(SimpleScheduleBuilder
                        .simpleSchedule() // 使用SimpleScheduleBuilder創建simpleSchedule
                        .withIntervalInSeconds(2) // 時間間隔為2秒
                        .withRepeatCount(99)) // 最多執行100次,此處需要注意,不包括第一次執行的
                .build();
        // 設置JobDetail(不再使用靜態方法)
        JobDetail jobDetail = JobBuilder.newJob((MyJobDetail.class)) // 使用JobBuilder創建JobDetail
                .withIdentity("jobDetail1", "group1")
                .usingJobData("user", "AlanShelby")
                .build();
        // 設置scheduler
        scheduler.scheduleJob(jobDetail, trigger);
        // 啟動、停止Scheduler
        scheduler.start();
        try {
            Thread.sleep(500000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        scheduler.shutdown();
    }

 

2.2 cronTrigger

1、適用於更為復雜的需求,它類似於Linux系統中的crontab,但要比crontab更強大。它基本上覆蓋了之前章節講到的三個類型的功能(並不是全部功能),相對於前三個類型,cronTrigger也更難理解。

2、它適合的任務類似於:每天0:00,9:00,18:00各執行一次。

3、它的屬性只有一個Cron表達式,下面有對cron表達式詳細的講解。

Trigger trigger = TriggerBuilder.newTrigger()
	.withIdentity("trigger1", "group1")
	.withSchedule(CronScheduleBuilder.cronSchedule("*/5 * * * * ?")) // 每5秒鍾執行一次
	.build();

  

3.job

Job是有可能並發執行的,比如一個任務要執行10秒中,而調度算法是每秒中觸發1次,那么就有可能多個任務被並發執行。

有時候我們並不想任務並發執行,比如這個任務要去”獲得數據庫中所有未發送郵件的名單”,如果是並發執行,就需要一個數據庫鎖去避免一個數據被多次處理。這個時候一個@DisallowConcurrentExecution解決這個問題。

@DisallowConcurrentExecution 
public class DoNothingJob implements Job {
    public void execute(JobExecutionContext context) throws JobExecutionException {
        System.out.println("do nothing");
    }
}

  

4.調度核心類QuartzSchedulerThread

/**
     * <p>
     * The main processing loop of the <code>QuartzSchedulerThread</code>.
     * </p>
     */
    @Override
    public void run() {
        int acquiresFailed = 0;

        while (!halted.get()) {
            try {
                // check if we're supposed to pause...
                synchronized (sigLock) {
                    while (paused && !halted.get()) {
                        try {
                            // wait until togglePause(false) is called...
                            sigLock.wait(1000L);
                        } catch (InterruptedException ignore) {
                        }

                        // reset failure counter when paused, so that we don't
                        // wait again after unpausing
                        acquiresFailed = 0;
                    }

                    if (halted.get()) {
                        break;
                    }
                }

                // wait a bit, if reading from job store is consistently
                // failing (e.g. DB is down or restarting)..
                if (acquiresFailed > 1) {
                    try {
                        long delay = computeDelayForRepeatedErrors(qsRsrcs.getJobStore(), acquiresFailed);
                        Thread.sleep(delay);
                    } catch (Exception ignore) {
                    }
                }

                int availThreadCount = qsRsrcs.getThreadPool().blockForAvailableThreads();
                if(availThreadCount > 0) { // will always be true, due to semantics of blockForAvailableThreads...

                    List<OperableTrigger> triggers;

                    long now = System.currentTimeMillis();

                    clearSignaledSchedulingChange();
                    try {
                        triggers = qsRsrcs.getJobStore().acquireNextTriggers(
                                now + idleWaitTime, Math.min(availThreadCount, qsRsrcs.getMaxBatchSize()), qsRsrcs.getBatchTimeWindow());
                        acquiresFailed = 0;
                        if (log.isDebugEnabled())
                            log.debug("batch acquisition of " + (triggers == null ? 0 : triggers.size()) + " triggers");
                    } catch (JobPersistenceException jpe) {
                        if (acquiresFailed == 0) {
                            qs.notifySchedulerListenersError(
                                "An error occurred while scanning for the next triggers to fire.",
                                jpe);
                        }
                        if (acquiresFailed < Integer.MAX_VALUE)
                            acquiresFailed++;
                        continue;
                    } catch (RuntimeException e) {
                        if (acquiresFailed == 0) {
                            getLog().error("quartzSchedulerThreadLoop: RuntimeException "
                                    +e.getMessage(), e);
                        }
                        if (acquiresFailed < Integer.MAX_VALUE)
                            acquiresFailed++;
                        continue;
                    }

                    if (triggers != null && !triggers.isEmpty()) {

                        now = System.currentTimeMillis();
                        long triggerTime = triggers.get(0).getNextFireTime().getTime();
                        long timeUntilTrigger = triggerTime - now;
                        while(timeUntilTrigger > 2) {
                            synchronized (sigLock) {
                                if (halted.get()) {
                                    break;
                                }
                                if (!isCandidateNewTimeEarlierWithinReason(triggerTime, false)) {
                                    try {
                                        // we could have blocked a long while
                                        // on 'synchronize', so we must recompute
                                        now = System.currentTimeMillis();
                                        timeUntilTrigger = triggerTime - now;
                                        if(timeUntilTrigger >= 1)
                                            sigLock.wait(timeUntilTrigger);
                                    } catch (InterruptedException ignore) {
                                    }
                                }
                            }
                            if(releaseIfScheduleChangedSignificantly(triggers, triggerTime)) {
                                break;
                            }
                            now = System.currentTimeMillis();
                            timeUntilTrigger = triggerTime - now;
                        }

                        // this happens if releaseIfScheduleChangedSignificantly decided to release triggers
                        if(triggers.isEmpty())
                            continue;

                        // set triggers to 'executing'
                        List<TriggerFiredResult> bndles = new ArrayList<TriggerFiredResult>();

                        boolean goAhead = true;
                        synchronized(sigLock) {
                            goAhead = !halted.get();
                        }
                        if(goAhead) {
                            try {
                                List<TriggerFiredResult> res = qsRsrcs.getJobStore().triggersFired(triggers);
                                if(res != null)
                                    bndles = res;
                            } catch (SchedulerException se) {
                                qs.notifySchedulerListenersError(
                                        "An error occurred while firing triggers '"
                                                + triggers + "'", se);
                                //QTZ-179 : a problem occurred interacting with the triggers from the db
                                //we release them and loop again
                                for (int i = 0; i < triggers.size(); i++) {
                                    qsRsrcs.getJobStore().releaseAcquiredTrigger(triggers.get(i));
                                }
                                continue;
                            }

                        }

                        for (int i = 0; i < bndles.size(); i++) {
                            TriggerFiredResult result =  bndles.get(i);
                            TriggerFiredBundle bndle =  result.getTriggerFiredBundle();
                            Exception exception = result.getException();

                            if (exception instanceof RuntimeException) {
                                getLog().error("RuntimeException while firing trigger " + triggers.get(i), exception);
                                qsRsrcs.getJobStore().releaseAcquiredTrigger(triggers.get(i));
                                continue;
                            }

                            // it's possible to get 'null' if the triggers was paused,
                            // blocked, or other similar occurrences that prevent it being
                            // fired at this time...  or if the scheduler was shutdown (halted)
                            if (bndle == null) {
                                qsRsrcs.getJobStore().releaseAcquiredTrigger(triggers.get(i));
                                continue;
                            }

                            JobRunShell shell = null;
                            try {
                                shell = qsRsrcs.getJobRunShellFactory().createJobRunShell(bndle);
                                shell.initialize(qs);
                            } catch (SchedulerException se) {
                                qsRsrcs.getJobStore().triggeredJobComplete(triggers.get(i), bndle.getJobDetail(), CompletedExecutionInstruction.SET_ALL_JOB_TRIGGERS_ERROR);
                                continue;
                            }

                            if (qsRsrcs.getThreadPool().runInThread(shell) == false) {
                                // this case should never happen, as it is indicative of the
                                // scheduler being shutdown or a bug in the thread pool or
                                // a thread pool being used concurrently - which the docs
                                // say not to do...
                                getLog().error("ThreadPool.runInThread() return false!");
                                qsRsrcs.getJobStore().triggeredJobComplete(triggers.get(i), bndle.getJobDetail(), CompletedExecutionInstruction.SET_ALL_JOB_TRIGGERS_ERROR);
                            }

                        }

                        continue; // while (!halted)
                    }
                } else { // if(availThreadCount > 0)
                    // should never happen, if threadPool.blockForAvailableThreads() follows contract
                    continue; // while (!halted)
                }

                long now = System.currentTimeMillis();
                long waitTime = now + getRandomizedIdleWaitTime();
                long timeUntilContinue = waitTime - now;
                synchronized(sigLock) {
                    try {
                      if(!halted.get()) {
                        // QTZ-336 A job might have been completed in the mean time and we might have
                        // missed the scheduled changed signal by not waiting for the notify() yet
                        // Check that before waiting for too long in case this very job needs to be
                        // scheduled very soon
                        if (!isScheduleChanged()) {
                          sigLock.wait(timeUntilContinue);
                        }
                      }
                    } catch (InterruptedException ignore) {
                    }
                }

            } catch(RuntimeException re) {
                getLog().error("Runtime error occurred in main trigger firing loop.", re);
            }
        } // while (!halted)

        // drop references to scheduler stuff to aid garbage collection...
        qs = null;
        qsRsrcs = null;
    }

  

4.1 blockForAvailableThreads

就是qsRsrcs.getThreadPool().blockForAvailableThreads(),如果線程池滿了的話,則會阻塞,因而會影響調度的准確性。

int availThreadCount = qsRsrcs.getThreadPool().blockForAvailableThreads();

獲取可用的線程數量。通常在第一次時候這個數量等於配置中配置的參數:

org.quartz.threadPool.threadCount = 20

  

4.2 maxBatchSize

triggers = qsRsrcs.getJobStore().acquireNextTriggers(
                                now + idleWaitTime, Math.min(availThreadCount, qsRsrcs.getMaxBatchSize()), qsRsrcs.getBatchTimeWindow());

這個參數的意思是批量查詢的數量,但並不是你配置多少它每次就能查詢多少,這算一個優化的配置項,因為在jdbc store的時候,減少對數據庫的輪詢次數算是一個比較大的優化;Math.min(availThreadCount, qsRsrcs.getMaxBatchSize()), qsRsrcs.getBatchTimeWindow())可以看到這里會取配置的批量的數和可用線程的最小數,所以批量數可以配置成和線程數大小一致:

org.quartz.threadPool.threadCount= 20
org.quartz.scheduler.batchTriggerAcquisitionMaxCount= 20  

  

 

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM