서론
지난 포스트에서는 Listener를 적용해보았다. 어떤 순서로 돌아가는지, 오류가 발생했을 때는 어떻게 되는지 궁금해서 수행중인 Job을 강제로 중단시키거나 오류를 발생시키고 싶어졌다.
그러나, Quartz에서 스케줄을 종료할 때 Scheduler.shutdown() 함수를 사용하는데, 이 함수는 Trigger를 종료시키는 명령으로써 이미 수행중인 Job에는 영향을 주지않는다. 따라서, 이미 진행되고 있는 Job은 멈출 수 없으므로 별도의 기능을 구현해야한다.
그래서 이번 포스트에서는 Job을 강제로 중단시키기는 방법에 대해서 다루어보려고한다.
개발환경
- jdk-11.0.5
- quartz-2.3.2
<!-- Scheduler -->
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId>
<version>2.3.2</version>
</dependency>
<!-- Scheduler -->
<!-- Logging -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.25</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
</dependency>
<!-- Logging -->
소스코드
1. MainJob.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
|
package com.dochi.quartz.interrupt;
import org.quartz.InterruptableJob;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.quartz.UnableToInterruptJobException;
public class MainJob implements InterruptableJob {
private Thread currentThread = null;
@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
// 현재 Thread 저장
this.currentThread = Thread.currentThread();
String jobName = context.getJobDetail().getKey().getName();
System.out.println(String.format("[%s][%s] Running...", this.getClass().getName(), jobName));
try {
// 강제로 종료를 지연시키기
Thread.sleep(5*1000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(String.format("[%s][%s] Finish!!!", this.getClass().getName(), jobName));
}
@Override
public void interrupt() throws UnableToInterruptJobException {
// interrupt 설정
// - 강제종료
if( this.currentThread != null ) {
this.currentThread.interrupt();
}
}
}
|
cs |
2. TriggerLogListener.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
|
package com.dochi.quartz.interrupt;
import java.util.Date;
import org.quartz.JobExecutionContext;
import org.quartz.Trigger;
import org.quartz.Trigger.CompletedExecutionInstruction;
import org.quartz.TriggerListener;
public class TriggerLogListener implements TriggerListener {
@Override
public String getName() {
return this.getClass().getName();
}
@Override
public void triggerFired(Trigger trigger, JobExecutionContext context) {
System.out.println(String.format("[%s][%s][triggerFired]", JobLauncher.TIMESTAMP_FMT.format(new Date()), getName()));
}
@Override
public boolean vetoJobExecution(Trigger trigger, JobExecutionContext context) {
System.out.println(String.format("[%s][%s][vetoJobExecution]", JobLauncher.TIMESTAMP_FMT.format(new Date()), getName()));
return false;
}
@Override
public void triggerMisfired(Trigger trigger) {
System.out.println(String.format("[%s][%s][triggerMisfired]", JobLauncher.TIMESTAMP_FMT.format(new Date()), getName()));
}
@Override
public void triggerComplete(Trigger trigger, JobExecutionContext context,
CompletedExecutionInstruction triggerInstructionCode) {
System.out.println(String.format("[%s][%s][triggerComplete]", JobLauncher.TIMESTAMP_FMT.format(new Date()), getName()));
}
}
|
cs |
3. JobLogListener.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
|
package com.dochi.quartz.interrupt;
import java.util.Date;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.quartz.JobListener;
public class JobLogListener implements JobListener {
@Override
public String getName() {
return this.getClass().getName();
}
@Override
public void jobToBeExecuted(JobExecutionContext context) {
System.out.println(String.format("[%s][%s][triggerComplete]", JobLauncher.TIMESTAMP_FMT.format(new Date()), getName()));
}
@Override
public void jobExecutionVetoed(JobExecutionContext context) {
System.out.println(String.format("[%s][%s][jobExecutionVetoed]", JobLauncher.TIMESTAMP_FMT.format(new Date()), getName()));
}
@Override
public void jobWasExecuted(JobExecutionContext context, JobExecutionException jobException) {
System.out.println(String.format("[%s][%s][jobWasExecuted]", JobLauncher.TIMESTAMP_FMT.format(new Date()), getName()));
}
}
|
cs |
4. JobLauncher.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
|
package com.dochi.quartz.interrupt;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Set;
import org.quartz.JobBuilder;
import org.quartz.JobDetail;
import org.quartz.JobKey;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.SchedulerFactory;
import org.quartz.SimpleScheduleBuilder;
import org.quartz.Trigger;
import org.quartz.TriggerBuilder;
import org.quartz.UnableToInterruptJobException;
import org.quartz.impl.StdSchedulerFactory;
import org.quartz.impl.matchers.GroupMatcher;
public class JobLauncher {
// 상수 설정
// - Prefix 설정
public static final String PREFIX_STEP_JOB_NAME = "job_";
public static final String PREFIX_STEP_TRIGGER_NAME = "trigger_";
public static final String PREFIX_NEXT_STEP_JOB_NAME = "step_job_";
public static final String PREFIX_NEXT_STEP_TRIGGER_NAME = "step_trigger_";
// - DateFormat 설정
public static final SimpleDateFormat TIMESTAMP_FMT = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
public static final SimpleDateFormat DATETIME_FMT = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
// Scheduler 객체 생성
private static SchedulerFactory factory = null;
private static Scheduler scheduler = null;
// Main 함수
public static void main(String[] args) throws SchedulerException {
// Scheduler 실행
start();
// Schedule 등록
addSchedule("MainJob");
addSchedule("MainJob2");
try {
System.out.println("아무키나 입력하면 종료됩니다...");
System.in.read();
// Scheduler 롱료
stop();
} catch (IOException e) {
e.printStackTrace();
}
}
// Scheduler 실행 함수
public static void start() throws SchedulerException {
// Scheduler 객체 정의
factory = new StdSchedulerFactory();
scheduler = factory.getScheduler();
// Listener 설정
scheduler.getListenerManager().addJobListener(new JobLogListener());
scheduler.getListenerManager().addTriggerListener(new TriggerLogListener());
// Scheduler 실행
scheduler.start();
}
// Scheduler 종료 함수
public static void stop() throws SchedulerException {
try {
System.out.println("스케줄러가 종료됩니다...");
// Job Key 목록
Set<JobKey> allJobKeys = scheduler.getJobKeys(GroupMatcher.anyGroup());
// Job 강제 중단
allJobKeys.forEach((jobKey)->{
try {
scheduler.interrupt(jobKey);
} catch (UnableToInterruptJobException e) {
e.printStackTrace();
}
});
// Scheduler 중단
// - true : 모든 Job이 완료될 때까지 대기 후 종료
// - false: 즉시 종료
scheduler.shutdown(true);
System.out.println("스케줄러가 종료되었습니다.");
} catch (SchedulerException e) {
e.printStackTrace();
}
}
// Schedule 등록 함수
public static void addSchedule(String name) throws SchedulerException {
// JobDetail 설정
JobDetail jobDetail = JobBuilder.newJob(MainJob.class)
.withIdentity(PREFIX_STEP_JOB_NAME+name)
.build();
// Simple Schedule 생성
// - 3초마다 실행, 최대 5회
SimpleScheduleBuilder schedule = SimpleScheduleBuilder.simpleSchedule()
.withIntervalInSeconds(3)
.withRepeatCount(5);
// Trigger 설정
Trigger trigger = TriggerBuilder.newTrigger()
.withIdentity(PREFIX_STEP_TRIGGER_NAME+name)
.withSchedule(schedule)
.forJob(jobDetail)
.build();
// Schedule 등록
scheduler.scheduleJob(jobDetail, trigger);
}
}
|
cs |
실행결과
- JobLauncher를 실행시키고, Console에서 아무키나 입력하면 Scheduler가 중단됨.
- 이미 Job이 실행중이라면 위와같이 InterInterruptedException이 발생함.
마치며
지금까지 Job을 강제로 중단하는 방법에 대해서 다루어보았다. Listener를 좀 더 알아보기위해서 만들었는데, 나중에 Crawler를 적용했을 때 요청이 오래걸리거나 잘못요청했을 때 중단하는 기능으로 활용하면 매우 좋을것 같다. 다음 포스트에서는 Listener를 활용한 예제를 다루어보도록 하겠다.
'Back-end > JAVA' 카테고리의 다른 글
[JAVA] Quartz 스케줄러 만들기 (5) - Crawler Job (0) | 2020.11.12 |
---|---|
[JAVA] Quartz 스케줄러 만들기 (4) - Step By Step (0) | 2020.11.11 |
[JAVA] Quartz 스케줄러 만들기 (2) - Listener (0) | 2020.11.10 |
[JAVA] Quartz 스케줄러 만들기 (1) - 실행 (0) | 2020.11.09 |
[JAVA] 간단한 HTTP 서버 구축하기 (0) | 2020.11.05 |
댓글