๐Ÿ“• CS ์Šคํ„ฐ๋””/Java, Spring

[Spring] โŒš Quartz ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ Scheduling

iknowDev 2023. 3. 9. 22:58

ํŠน์ • ์‹œ๊ฐ„ ๊ฐ„๊ฒฉ์„ ๋ฐ˜๋ณตํ•˜๊ฑฐ๋‚˜ ํŠน์ •ํ•œ ์‹œ๊ฐ์— ๋กœ์ง์„ ์ˆ˜ํ–‰ํ•˜๋Š” ๊ฒƒ์„ Scheduler๋ผ๊ณ  ํ•ฉ๋‹ˆ๋‹ค.

์ด๋Ÿฌํ•œ ์ž‘์—…์„ ์œ„ํ•ด Spring์—์„œ๋Š” 2๊ฐ€์ง€ ์˜ต์…˜์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.

  1. Spring Scheduler
  2. Spring Quartz

๊ทธ ์ค‘ ์„ธ๋ฐ€ํ•œ ์ œ์–ด๊ฐ€ ๊ฐ€๋Šฅํ•œ Quartz Scheduler ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์— ๋Œ€ํ•ด ์•Œ์•„๋ด…์‹œ๋‹ค.

 

 

๊ธฐ๋ณธ ๊ตฌ์„ฑ

Job ์Šค์ผ€์ค„๋งํ•  ์‹ค์ œ ์ž‘์—…์„ ๊ฐ€์ง€๋Š” ๊ฐ์ฒด
- JobListener
JobData Job์—์„œ ์‚ฌ์šฉํ•  ๋ฐ์ดํ„ฐ๋ฅผ ์ „๋‹ฌํ•˜๋Š” ๊ฐ์ฒด
JobDataMap
์Šค์ผ€์ค„๋Ÿฌ์—์„œ Job ์‹คํ–‰ ์‹œ ์‚ฌ์šฉํ•  ๋ณ€์ˆ˜ ๊ฐ’ ์ „๋‹ฌ ์šฉ๋„ (key-value ํ˜•์‹)
JobDetail
Job์˜ ์ •๋ณด๋ฅผ ๊ตฌ์„ฑํ•˜๋Š” ๊ฐ์ฒด
JobBuilder Job์˜ instances ์ •์˜ํ•˜๋Š” JobDetail instances ๋นŒ๋“œ์— ์‚ฌ์šฉ
Trigger
Job์ด ์–ธ์ œ ์‹œํ–‰๋ ์ง€๋ฅผ ๊ตฌ์„ฑํ•˜๋Š” ๊ฐ์ฒด
- TriggerListener
TriggerBuilder Trigger instances ๋นŒ๋“œ์— ์‚ฌ์šฉ
Scheduler JobDetail๊ณผ Trigger ์ •๋ณด๋ฅผ ์ด์šฉํ•ด์„œ Job์„ ์‹œ์Šคํ…œ์— ๋“ฑ๋กํ•˜๊ณ , Trigger๊ฐ€ ๋™์ž‘ํ•˜๋ฉด ์ง€์ •๋œ Job์„ ์‹คํ–‰์‹œํ‚ค๋Š” ๊ฐ์ฒด
SchedulerFactory Scheduler ์ธ์Šคํ„ด์Šค๋ฅผ ์ƒ์„ฑํ•˜๋Š” ๊ฐ์ฒด
quartz.properties Quartz ์Šค์ผ€์ค„๋Ÿฌ๋ฅผ ์œ„ํ•œ ์„ค์ •๊ฐ’ ๊ตฌ์„ฑ ํŒŒ์ผ
JobStore ์Šค์ผ€์ค„๋Ÿฌ์— ๋“ฑ๋ก๋œ Job์˜ ์ •๋ณด์™€ ์‹คํ–‰์ด๋ ฅ์ด ์ €์žฅ๋˜๋Š” ๊ณต๊ฐ„
JobExecutionContext
execute ๋ฉ”์„œ๋“œ์˜ ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ๋„˜์–ด๊ฐ€๋Š” ์ธ์ž์ด๋‹ค. JobDetail ์ธ์Šคํ„ด์Šค๊ฐ€ Scheduler์— ์˜ํ•ด ์‹คํ–‰๋ ๋•Œ ๋„˜์–ด์˜ค๊ณ , ์‹คํ–‰์ด ์™„๋ฃŒ๋œ ๋’ค์—๋Š” Trigger๋กœ ๋„˜์–ด๊ฐ„๋‹ค.

 

Quartz Scheduler Flow

 

์˜ˆ์‹œ ์ฝ”๋“œ

์Šค์ผ€์ฅด๋ง๋œ ์‹œ๊ฐ„์— ๋ฐฐ์น˜ ์ž‘์—…์„ ํ•˜๊ธฐ ์œ„ํ•ด์„œ

import java.util.Date;

import org.quartz.Job;
import org.quartz.JobDataMap;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.quartz.PersistJobDataAfterExecution;

@PersistJobDataAfterExecution
public class SchedulerTest implements Job{
	@Override
	public void execute(JobExecutionContext context) throws JobExecutionException {

        JobDataMap dataMap = context.getJobDetail().getJobDataMap();
        int idx =  dataMap.getInt("idx");
    	
        System.out.println("* " + idx + " ์Šค์ผ€์ค„๋Ÿฌ ์ˆ˜ํ–‰ [" + new Date(System.currentTimeMillis()) + "]");

        dataMap.putAsString("idx", idx+1);

        if (5 <= idx) { // ์ˆ˜ํ–‰ ํšŸ์ˆ˜ ์ง€์ •
            JobExecutionException e = new JobExecutionException("End Repeat");
            e.setUnscheduleAllTriggers(true);
            throw e;
        }
    }
}
import org.quartz.CronScheduleBuilder;
import org.quartz.CronTrigger;
import org.quartz.JobBuilder;
import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.SchedulerFactory;
import org.quartz.SimpleScheduleBuilder;
import org.quartz.SimpleTrigger;
import org.quartz.TriggerBuilder;
import org.quartz.impl.StdSchedulerFactory;

public class Simpletest {
	public static void main(String[] args) {
        SchedulerFactory schedulerFactory = new StdSchedulerFactory();
        try {
            Scheduler scheduler = schedulerFactory.getScheduler();

            JobDetail job = JobBuilder.newJob(SchedulerTest.class)
            		.withIdentity("jobName", Scheduler.DEFAULT_GROUP)
            		.usingJobData("idx", 1)
            		.build();

            // 5์ดˆ ์ฃผ๊ธฐ Simple ์Šค์ผ€์ค„๋ง
            SimpleTrigger simplTrigger = TriggerBuilder.newTrigger()
            		  .withIdentity("trigger1", Scheduler.DEFAULT_GROUP)
            		  .withSchedule(SimpleScheduleBuilder.simpleSchedule()
            				  .withIntervalInSeconds(5)
            				  .withRepeatCount(3)) // ๋ฐ˜๋ณต ํšŸ์ˆ˜
            		  .build();

            // 10์ดˆ ์ฃผ๊ธฐ Cron ์Šค์ผ€์ค„๋ง
            CronTrigger cronTrigger = TriggerBuilder.newTrigger()
            		  .withIdentity("trigger2", Scheduler.DEFAULT_GROUP)
            		  .withSchedule(CronScheduleBuilder.cronSchedule("0/10 * * * * ?"))
            		  .build();

            scheduler.scheduleJob(job, cronTrigger);
            scheduler.start();
        } catch (Exception e) {
            e.printStackTrace();
        }
	}
}

SimpleTrigger (3์ดˆ ๊ฐ„๊ฒฉ ์ž‘์—… ์ˆ˜ํ–‰ ๋ฐ˜๋ณต)

 

Cron Trigger (10์ดˆ ๊ฐ„๊ฒฉ ์ž‘์—… ์ˆ˜ํ–‰ ๋ฐ˜๋ณต)

 

Cron Trigger Expression

CronTrigger๋Š” “๋งค์ผ ์›”์š”์ผ 9์‹œ”, ๋˜๋Š” “๋งค ์ฃผ๋ง ์˜คํ›„ 7์‹œ”, “1์›” ํ•œ๋‹ฌ๊ฐ„ ๋งค์ฃผ ํ† ์š”์ผ ์˜คํ›„ 12์‹œ์—์„œ 1์‹œ ์‚ฌ์ด 10๋ถ„ ๊ฐ„๊ฒฉ”๊ณผ ๊ฐ™์ด ์Šค์ผ€์ค„ ์„ค์ •์„ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

  1. ์ดˆ(Seconds)
  2. ๋ถ„(Minutes)
  3. ์‹œ(Hours)
  4. ์ผ(Day-of-Month)
  5. ์›”(Months)
  6. ์š”์ผ(Days-of-Week)
  7. ์—ฐ๋„(Year) - optional
? ์„ค์ • ๊ฐ’ ์—†์Œ์— ์‚ฌ์šฉ
์ผ(day-of-month)์™€ ์š”์ผ(day-of-week) ํ•„๋“œ์—์„œ๋งŒ ํ—ˆ์šฉ
/ ๊ฐ’์˜ ์ฆ๊ฐ€ ํ‘œํ˜„์— ์‚ฌ์šฉ
๋ถ„(Minutes) ํ•„๋“œ์— ‘0/10’ ๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค๋ฉด, ์ด๊ฒƒ์€ “0๋ถ„ ๋ถ€ํ„ฐ ์‹œ์ž‘ํ•˜์—ฌ (์‹œ๊ฐ„์˜) ๋งค 10๋ถ„ ๋งˆ๋‹ค”๋ฅผ ์˜๋ฏธ
L ์ผ(day-of-month) ํ•„๋“œ์—์„œ ์‚ฌ์šฉ๋˜์—ˆ์„ ๊ฒฝ์šฐ ์ด๋‹ฌ์˜ ๋งˆ์ง€๋ง‰ ๋‚ ์„ ์˜๋ฏธ
1์›”์ด๋ฉด 31์ผ์ด ๋˜๊ณ , 2์›”์€ 28์ผ(์œค๋…„ 29์ผ)์ด ๋จ
"6L” ๋˜๋Š” “FRIL”์€ “์ด๋‹ฌ์˜ ๋งˆ์ง€๋ง‰ ๊ธˆ์š”์ผ”์„ ์˜๋ฏธ
W ์ฃผ์–ด์ง„ ๋‚ ๋กœ๋ถ€ํ„ฐ ๊ฐ€์žฅ ๊ฐ€๊นŒ์šด ํ‰์ผ(์›”์š”์ผ~๊ธˆ์š”์ผ)
“21W”๋ผ๋Š” ๊ฐ’์„ ์ผ(day-of-month)ํ•„๋“œ์— ์„ค์ •ํ•˜๋ฉด, “์ด๋‹ฌ์˜ 21๋ฒˆ์งธ๋‚ ์—์„œ ๊ฐ€์žฅ ๊ฐ€๊นŒ์šด ํ‰์ผ”์„ ์˜๋ฏธ
# ์ด๋‹ฌ์˜ n๋ฒˆ์งธ X์š”์ผ
“2#1” ๋˜๋Š” “MON#1”์„ ์š”์ผ(day-of-week) ํ•„๋“œ์— ์„ค์ •ํ•  ๊ฒฝ์šฐ ์ด๊ฒƒ์€ “์ด๋‹ฌ์˜ ์ฒซ๋ฒˆ์งธ ์›”์š”์ผ”์„ ์˜๋ฏธ

 

์ถœ์ฒ˜

Expression Meaning
0 0 12 * * ? Fire at 12pm (noon) every day
0 15 10 ? * * Fire at 10:15am every day
0 15 10 * * ? Fire at 10:15am every day
0 15 10 * * ? * Fire at 10:15am every day
0 15 10 * * ? 2005 Fire at 10:15am every day during the year 2005
0 * 14 * * ? Fire every minute starting at 2pm and ending at 2:59pm, every day
0 0/5 14 * * ? Fire every 5 minutes starting at 2pm and ending at 2:55pm, every day
0 0/5 14,18 * * ? Fire every 5 minutes starting at 2pm and ending at 2:55pm, AND fire every 5 minutes starting at 6pm and ending at 6:55pm, every day
0 0-5 14 * * ? Fire every minute starting at 2pm and ending at 2:05pm, every day
0 10,44 14 ? 3 WED Fire at 2:10pm and at 2:44pm every Wednesday in the month of March.
0 15 10 ? * MON-FRI Fire at 10:15am every Monday, Tuesday, Wednesday, Thursday and Friday
0 15 10 15 * ? Fire at 10:15am on the 15th day of every month
0 15 10 L * ? Fire at 10:15am on the last day of every month
0 15 10 L-2 * ? Fire at 10:15am on the 2nd-to-last last day of every month
0 15 10 ? * 6L Fire at 10:15am on the last Friday of every month
0 15 10 ? * 6L Fire at 10:15am on the last Friday of every month
0 15 10 ? * 6L 2002-2005 Fire at 10:15am on every last friday of every month during the years 2002, 2003, 2004 and 2005
0 15 10 ? * 6#3 Fire at 10:15am on the third Friday of every month
0 0 12 1/5 * ? Fire at 12pm (noon) every 5 days every month, starting on the first day of the month.
0 11 11 11 11 ? Fire every November 11th at 11:11am.

http://www.cronmaker.com/ ๋งํฌ๋ฅผ ํ†ตํ•ด Cron ํ‘œํ˜„์‹์„ ๋งŒ๋“ค์–ด ๋ณผ ์ˆ˜ ์žˆ๋‹ค.

 

 

์„ค์ •์„ ์œ„ํ•œ pom.xml (์ฝ”๋“œ ๋ณด๊ธฐ)
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>quartz_test</groupId>
  <artifactId>simple01</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  
  <properties>
      <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
      <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
      <java-version>1.7</java-version>
      <org.springframework-version>4.2.3.RELEASE</org.springframework-version>
      <maven.test.skip>true</maven.test.skip>
      <cobertura.skip>true</cobertura.skip>
  </properties>
  
  <dependencies>
	  <dependency>
	      <groupId>org.springframework</groupId>
	      <artifactId>spring-jdbc</artifactId>
	      <version>${org.springframework-version}</version>
	  </dependency>
	  <dependency>
	      <groupId>org.springframework</groupId>
	      <artifactId>spring-context-support</artifactId>
	      <version>${org.springframework-version}</version>
	  </dependency>
	  <dependency>
	      <groupId>commons-logging</groupId>
	      <artifactId>commons-logging</artifactId>
	      <version>1.2</version>
	  </dependency>
	  <dependency>
	      <groupId>org.springframework</groupId>
	      <artifactId>spring-tx</artifactId>
	      <version>${org.springframework-version}</version>
	  </dependency>
	  <dependency>
	      <groupId>org.springframework</groupId>
	      <artifactId>spring-test</artifactId>
	      <version>${org.springframework-version}</version>
	  </dependency>
	
	  <!-- quartz scheduler -->
	  <dependency>
	      <groupId>org.quartz-scheduler</groupId>
	      <artifactId>quartz</artifactId>
	      <version>2.2.1</version>
	  </dependency>
  </dependencies>
  
  <build>
  	<plugins>
	    <!-- Set a compiler level -->
	    <plugin>
	        <groupId>org.apache.maven.plugins</groupId>
	        <artifactId>maven-compiler-plugin</artifactId>
	        <version>3.2</version>
	        <configuration>
	            <source>${java-version}</source>
	            <target>${java-version}</target>
	            <compilerArgument>-Xlint:all</compilerArgument>
	            <showWarnings>true</showWarnings>
	            <showDeprecation>true</showDeprecation>
	        </configuration>
	    </plugin>
  	</plugins>
  </build>
</project>