๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ
Spring/Spring Batch

Spring Batch (8) JobLauncher

by ์ฃผ๋ฐœ2 2022. 11. 11.
๋ฐ˜์‘ํ˜•

๐Ÿ“Ž JobLauncher

์ง€๋‚œ ์‹œ๊ฐ„์—๋Š” JobRepository์— ๋Œ€ํ•ด ์‚ดํŽด๋ณด์•˜์Šต๋‹ˆ๋‹ค.

JobRepository๋Š” Spring Batch ์ž‘์—…์„ ์ง„ํ–‰ํ•˜๋ฉด์„œ JobInstance, JobExecution, StepExeuction ๋“ฑ Batch์™€ ๊ด€๋ จ๋œ ๋„๋ฉ”์ธ์˜ CRUD ์ฒ˜๋ฆฌ๋ฅผ ํ•˜๋Š” ์—ญํ• ์„ ํ•ฉ๋‹ˆ๋‹ค.

 

์ด๋ฒˆ ์‹œ๊ฐ„์—๋Š” Spring Batch Job์˜ ์‹คํ–‰ ์ฃผ์ฒด์ธ JobLauncher์— ๋Œ€ํ•ด ์‚ดํŽด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

 

 

๐Ÿ“Œ JobLauncher

๊ธฐ๋ณธ ๊ฐœ๋…

  • Spring Batch Job์„ ์‹คํ–‰์‹œํ‚ค๋Š” ์—ญํ• 
  • Job & JobParameters๋ฅผ ์ธ์ž๋กœ ๋ฐ›์œผ๋ฉฐ ์š”์ฒญ๋œ ๋ฐฐ์น˜ ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•œ ํ›„ JobExecution์„ ๋ฐ˜ํ™˜
  • Spring Batch์—์„œ๋Š” BatchAutoConfiguration ํด๋ž˜์Šค ๋‚ด์— ์กด์žฌํ•˜๋Š” JobLauncherApplicationRunner ํด๋ž˜์Šค๊ฐ€ ์ž๋™์œผ๋กœ JobLauncher์„ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค.

76 ๋ผ์ธ์—์„œ JobLauncherApplicationRunner ๊ฐ์ฒด ์ƒ์„ฑ
JobLauncherApplicationRunner ํด๋ž˜์Šค - 199๋ผ์ธ์˜ run() ๋ฉ”์„œ๋“œ๋ฅผ ํ†ตํ•ด Job ์‹คํ–‰

Spring Batch Job์€ SyncTaskExecutor(๋™๊ธฐ), SipmleAsyncTaskExecutor(๋น„๋™๊ธฐ), ์ปค์Šคํ…€ ๊ตฌํ˜„์ฒด ๋“ฑ์˜ ์„ค์ •์„ ํ†ตํ•ด ๋™๊ธฐ ๋ฐ ๋น„๋™๊ธฐ ์‹คํ–‰์ด ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.

 

 

 

๐Ÿ“Œ JobLauncher ๋™์ž‘ ๊ณผ์ •

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
@RequiredArgsConstructor
@Configuration
public class JobLauncherConfiguration {
 
    private final JobBuilderFactory jobBuilderFactory;
    private final StepBuilderFactory stepBuilderFactory;
 
    @Bean
    public Job jobLauncherJob() {
        return this.jobBuilderFactory.get("jobLauncherJob")
                .start(jobLauncherStep1())
                .next(jobLauncherStep2())
                .incrementer(new RunIdIncrementer())
                .build();
    }
 
    @Bean
    public Step jobLauncherStep1() {
        return stepBuilderFactory.get("jobLauncherStep1")
                .tasklet(new Tasklet() {
                    @Override
                    public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception {
                        Thread.sleep(3000);
                        return RepeatStatus.FINISHED;
                    }
                })
                .build();
    }
 
    @Bean
    public Step jobLauncherStep2() {
        return stepBuilderFactory.get("jobLauncherStep2")
                .tasklet((contribution, chunkContext) -> null)
                .build();
    }
}
 
cs

์œ„์˜ ๊ฐ„๋‹จํ•œ Job, Step ์ฝ”๋“œ๋ฅผ ํ†ตํ•ด JobLauncher์— ๋Œ€ํ•ด ์‚ดํŽด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

 

 

Job์„ ์‹คํ–‰์‹œํ‚ค๋Š” JobLauncherApplicationRunner ๊ฐ์ฒด๋Š” BatchAutoConfiguration ํด๋ž˜์Šค์—์„œ ์ƒ์„ฑ์ด ๋ฉ๋‹ˆ๋‹ค.

์œ„ ์‚ฌ์ง„์€ JobLauncherApplicationRunner ํด๋ž˜์Šค์—์„œ Job์ด ์‹คํ–‰๋˜๋Š” ๊ณผ์ •์ž…๋‹ˆ๋‹ค.

run() ๋ฉ”์„œ๋“œ์—์„œ args ์ธ์ž๋Š” ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ๋ฐ›์€ ๋ณ€์ˆ˜๊ฐ€ ์กด์žฌํ•˜๋ฉด ํ•ด๋‹น ๊ฐ’์„ ์„ค์ •ํ•˜๋Š” ์—ญํ• ์„ ํ•ฉ๋‹ˆ๋‹ค.

 

 

launchJobFromProperties() ๋ฉ”์„œ๋“œ๊ฐ€ ํ˜ธ์ถœํ•˜๋Š” executeLocalJobs() ๋ฉ”์„œ๋“œ์˜ ๋กœ์ง์ž…๋‹ˆ๋‹ค.

 

์œ„ ์ฝ”๋“œ์—์„œ for ๋ฌธ ๋‚ด๋ถ€์— ์กด์žฌํ•˜๋Š” this.jobs๋Š” ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ฝ”๋“œ ์ „์ฒด์— ์กด์žฌํ•˜๋Š” Job์„ ๋‚˜ํƒ€๋‚ด๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

์ €๋Š” ์œ„์™€ ๊ฐ™์ด ์ด์ „ ์˜ˆ์ œ ์ฝ”๋“œ๋„ ์ „๋ถ€ ํฌํ•จ๋˜์–ด ์žˆ๊ธฐ์— this.jobs์˜ size๊ฐ€ 8์ด๊ณ , ํ•ด๋‹น job ์ค‘์—์„œ ํ˜„์žฌ ์‹คํ–‰ํ•œ job์˜ name๊ณผ ๋™์ผํ•œ์ง€ ๋น„๊ตํ•˜์—ฌ ๋™์ผํ•  ๊ฒฝ์šฐ execute(job, jobParameters) ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•ฉ๋‹ˆ๋‹ค.

 

 

execute() ๋ฉ”์„œ๋“œ ๋‚ด์—์„œ๋Š” JobParameters ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•˜๊ณ , jobLauncher.run() ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•˜์—ฌ Job์„ ์‹คํ–‰์‹œํ‚ต๋‹ˆ๋‹ค.

 

 

์œ„ run() ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•  ๋•Œ JobLauncher ์ธํ„ฐํŽ˜์ด์Šค์˜ ๊ตฌํ˜„์ฒด์ธ SimpleJobLauncher ํด๋ž˜์Šค์—์„œ run() ๋ฉ”์„œ๋“œ๋ฅผ ์˜ค๋ฒ„๋ผ์ด๋“œ ํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

 

์œ„ ์ฝ”๋“œ์—์„œ JobRepository์—์„œ jobName + jobKey ์กฐํ•ฉ์œผ๋กœ JobInstance, JobExecution์„ ์กฐํšŒํ•˜๊ธฐ ๋•Œ๋ฌธ์— lastExecution์€ null๋กœ ๋ฆฌํ„ดํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

 

 

 

SimpleJobLauncher ํด๋ž˜์Šค์˜ run() ์ „์ฒด ๋ฉ”์„œ๋“œ๋ฅผ ์‚ดํŽด๋ณด๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

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
    /**
     * Run the provided job with the given {@link JobParameters}. The
     * {@link JobParameters} will be used to determine if this is an execution
     * of an existing job instance, or if a new one should be created.
     *
     * @param job the job to be run.
     * @param jobParameters the {@link JobParameters} for this particular
     * execution.
     * @return the {@link JobExecution} if it returns synchronously. If the
     * implementation is asynchronous, the status might well be unknown.
     * @throws JobExecutionAlreadyRunningException if the JobInstance already
     * exists and has an execution already running.
     * @throws JobRestartException if the execution would be a re-start, but a
     * re-start is either not allowed or not needed.
     * @throws JobInstanceAlreadyCompleteException if this instance has already
     * completed successfully
     * @throws JobParametersInvalidException thrown if jobParameters is invalid.
     */
    @Override
    public JobExecution run(final Job job, final JobParameters jobParameters)
            throws JobExecutionAlreadyRunningException, JobRestartException, JobInstanceAlreadyCompleteException,
            JobParametersInvalidException {
 
        Assert.notNull(job, "The Job must not be null.");
        Assert.notNull(jobParameters, "The JobParameters must not be null.");
 
        final JobExecution jobExecution;
        JobExecution lastExecution = jobRepository.getLastJobExecution(job.getName(), jobParameters);
        if (lastExecution != null) {
            if (!job.isRestartable()) {
                throw new JobRestartException("JobInstance already exists and is not restartable");
            }
            /*
             * validate here if it has stepExecutions that are UNKNOWN, STARTING, STARTED and STOPPING
             * retrieve the previous execution and check
             */
            for (StepExecution execution : lastExecution.getStepExecutions()) {
                BatchStatus status = execution.getStatus();
                if (status.isRunning() || status == BatchStatus.STOPPING) {
                    throw new JobExecutionAlreadyRunningException("A job execution for this job is already running: "
                            + lastExecution);
                } else if (status == BatchStatus.UNKNOWN) {
                    throw new JobRestartException(
                            "Cannot restart step [" + execution.getStepName() + "] from UNKNOWN status. "
                                + "The last execution ended with a failure that could not be rolled back, "
                                + "so it may be dangerous to proceed. Manual intervention is probably necessary.");
                }
            }
        }
 
        // Check the validity of the parameters before doing creating anything
        // in the repository...
        job.getJobParametersValidator().validate(jobParameters);
 
        /*
         * There is a very small probability that a non-restartable job can be
         * restarted, but only if another process or thread manages to launch
         * <i>and</i> fail a job execution for this instance between the last
         * assertion and the next method returning successfully.
         */
        jobExecution = jobRepository.createJobExecution(job.getName(), jobParameters);
 
        try {
            taskExecutor.execute(new Runnable() {
 
                @Override
                public void run() {
                    try {
                        if (logger.isInfoEnabled()) {
                            logger.info("Job: [" + job + "] launched with the following parameters: [" + jobParameters
                                    + "]");
                        }
                        job.execute(jobExecution);
                        if (logger.isInfoEnabled()) {
                            Duration jobExecutionDuration = BatchMetrics.calculateDuration(jobExecution.getStartTime(), jobExecution.getEndTime());
                            logger.info("Job: [" + job + "] completed with the following parameters: [" + jobParameters
                                    + "] and the following status: [" + jobExecution.getStatus() + "]"
                                    + (jobExecutionDuration == null ? "" : " in " + BatchMetrics.formatDuration(jobExecutionDuration)));
                        }
                    }
                    catch (Throwable t) {
                        if (logger.isInfoEnabled()) {
                            logger.info("Job: [" + job
                                    + "] failed unexpectedly and fatally with the following parameters: [" + jobParameters
                                    + "]", t);
                        }
                        rethrow(t);
                    }
                }
 
                private void rethrow(Throwable t) {
                    if (t instanceof RuntimeException) {
                        throw (RuntimeException) t;
                    }
                    else if (t instanceof Error) {
                        throw (Error) t;
                    }
                    throw new IllegalStateException(t);
                }
            });
        }
        catch (TaskRejectedException e) {
            jobExecution.upgradeStatus(BatchStatus.FAILED);
            if (jobExecution.getExitStatus().equals(ExitStatus.UNKNOWN)) {
                jobExecution.setExitStatus(ExitStatus.FAILED.addExitDescription(e));
            }
            jobRepository.update(jobExecution);
        }
 
        return jobExecution;
    }
cs

 

 

์‹ค์ œ Job์„ ์‹คํ–‰ํ•˜๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉ๋˜๋Š” ์ธํ„ฐํŽ˜์ด์Šค๋Š” TaskExecutor ์ด๊ณ , ์ต๋ช…ํด๋ž˜์Šค๋กœ ๊ตฌํ˜„ํ•˜์—ฌ ๋™๊ธฐ ์‹คํ–‰์„ ํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

 

๋งŒ์•ฝ Job์„ ๋น„๋™๊ธฐ๋กœ ์‹คํ–‰ํ•˜๋ ค๋ฉด setTaskExecutor() ๋ฉ”์„œ๋“œ๋ฅผ ํ†ตํ•ด ์ง์ ‘ ์„ค์ •ํ•ด์ฃผ๋ฉด ๋น„๋™๊ธฐ ์‹คํ–‰์ด ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.

 

 

์ฐธ๊ณ  ๋ฌธ์„œ

๋ฐ˜์‘ํ˜•

'Spring > Spring Batch' ์นดํ…Œ๊ณ ๋ฆฌ์˜ ๋‹ค๋ฅธ ๊ธ€

Spring Batch (10) @JobScope, @StepScope  (0) 2022.12.08
Spring Batch (9) JobParametersValidator  (0) 2022.11.13
Spring Batch (7) JobRepository  (0) 2022.11.08
Spring Batch (6) ExecutionContext  (0) 2022.11.05
Spring Batch (5) Step, StepExecution  (0) 2022.11.03

๋Œ“๊ธ€