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

Spring Batch (4) Job, JobInstance, JobParameters, JobExecution

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

๐Ÿ“Ž Job, JobInstance, JobParameters, JobExecution

์ง€๋‚œ ์‹œ๊ฐ„์—๋Š” Spring Batch์˜ Meta Data Schema์— ๋Œ€ํ•ด ์‚ดํŽด๋ณด์•˜์Šต๋‹ˆ๋‹ค.

์ด๋ฒˆ ์‹œ๊ฐ„์—๋Š” Job์˜ ์—ฌ๋Ÿฌ ๋„๋ฉ”์ธ ๊ฐ์ฒด์ธ JobInstance, JobParameters, JobExecution ๊ฐ์ฒด์— ๋Œ€ํ•ด ์‚ดํŽด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

(์˜ˆ์ œ ์ฝ”๋“œ๋Š” GitHub์—์„œ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๐Ÿ˜ƒ)

 

 

๐Ÿ“Œ ํ•ด๋‹น ํฌ์ŠคํŒ…์˜ ๋ชฉํ‘œ

  • Spring Batch Job
  • Spring Batch JobInstance
  • Spring Batch JobParameters
  • Spring Batch JobExecution

 

 

๐Ÿ“Œ Job

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

  • ๋ฐฐ์น˜ ๊ณ„์ธต ๊ตฌ์กฐ์—์„œ ์ตœ์ƒ์œ„์˜ ๊ฐœ๋…์œผ๋กœ ์ „์ฒด ๋ฐฐ์น˜ ํ”„๋กœ์„ธ์Šค๋ฅผ ์บก์Šํ™”ํ•œ ์—”ํ‹ฐํ‹ฐ
  • Job Configuration์„ ํ†ตํ•ด ์ƒ์„ฑ๋˜๋Š” ๊ฐ์ฒด ๋‹จ์œ„๋กœ ๋ฐฐ์น˜ ์ž‘์—…์„ ์–ด๋–ป๊ฒŒ ๊ตฌ์„ฑํ•˜๊ณ  ์‹คํ–‰ํ• ์ง€ ๋ช…์„ธํ•ด ๋†“์€ ๊ฐ์ฒด 
  • ์—ฌ๋Ÿฌ Step๋“ค์„ ํฌํ•จํ•˜๋Š” ์ปจํ…Œ์ด๋„ˆ๋กœ ๋ฐ˜๋“œ์‹œ ํ•œ ๊ฐœ ์ด์ƒ์˜ Step์œผ๋กœ ๊ตฌ์„ฑํ•ด์•ผ ํ•จ

 

๊ธฐ๋ณธ ๊ตฌํ˜„์ฒด

 

AbstractJob

Job์„ ๊ตฌํ˜„ํ•˜๊ณ  ์žˆ๋Š” ์ถ”์ƒ ํด๋ž˜์Šค๋กœ ์—ฌ๋Ÿฌ ํ•„๋“œ ๋ฐ ๋ฉ”์„œ๋“œ๋“ค์ด ์กด์žฌํ•ฉ๋‹ˆ๋‹ค.

  • name: Job ์ด๋ฆ„
  • restartable: Job์˜ ์žฌ์‹œ์ž‘ ์—ฌ๋ถ€(default = true)
  • JobRepository: ๋ฉ”ํƒ€ ๋ฐ์ดํ„ฐ ์ €์žฅ์†Œ
  • JobExecutionListener: Job์˜ ์ด๋ฒคํŠธ ๋ฆฌ์Šค๋„ˆ
  • JobParametersIncrementer: JobParameters ์ฆ๊ฐ€๊ธฐ
  • JobParametersValidator: JobParameters ๊ฒ€์ฆ๊ธฐ
  • StepHandler: Step ์‹คํ–‰ ํ•ธ๋“ค๋Ÿฌ

 

SimpleJob

  • ์ˆœ์ฐจ์ ์œผ๋กœ Step์„ ์‹คํ–‰์‹œํ‚ค๋Š” Job
  • ๋ชจ๋“  Job์—์„œ ์œ ์šฉํ•˜๊ฒŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ํ‘œ์ค€ ๊ธฐ๋Šฅ์„ ํฌํ•จ

 

FlowJob

  • ํŠน์ • ์กฐ๊ฑด ๋ฐ ํ๋ฆ„์— ๋”ฐ๋ผ Step์„ ๊ตฌ์„ฑํ•˜์—ฌ ์‹คํ–‰ํ•˜๋Š” Job
  • Flow ๊ฐ์ฒด๋ฅผ ์‹คํ–‰์‹œ์ผœ ์ž‘์—…์„ ์ง„ํ–‰

 

 

๐Ÿ“Œ JobInstance

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

  • Job์ด ์‹คํ–‰๋˜๋Š” ์‹œ์ ์—์„œ ์ƒ์„ฑ๋˜๋Š” Job์˜ ๋…ผ๋ฆฌ์  ์ž‘์—… ์‹คํ–‰ ๊ฐœ๋…
  • Job์ด ์‹คํ–‰๋˜๋Š” ์‹œ์ ์— ์ฒ˜๋ฆฌํ•˜๋Š” ๋‚ด์šฉ์€ ๊ฐ๊ฐ ๋‹ค๋ฅด๊ธฐ ๋•Œ๋ฌธ์— Job์˜ ์‹คํ–‰์„ ๊ตฌ๋ถ„ํ•ด์•ผ ํ•จ
    • ์˜ˆ๋ฅผ ๋“ค์–ด, ํ•˜๋ฃจ์— ํ•œ ๋ฒˆ ๋งค์ผ Job์ด ์‹คํ–‰๋œ๋‹ค๋ฉด, ์‹คํ–‰๋˜๋Š” ๊ฐ๊ฐ์˜ Job์„ JobInstance๋ผ๊ณ  ํ•จ
  • ์ฒ˜์Œ ์‹œ์ž‘ํ•˜๋Š” Job & JobParameters์˜ ๊ฒฝ์šฐ ์ƒˆ๋กœ์šด JobInstance๋ฅผ ์ƒ์„ฑ
  • ์ด์ „๊ณผ ๋™์ผํ•œ Job & JobParameters์œผ๋กœ ์‹คํ–‰ํ•  ๊ฒฝ์šฐ ์ด๋ฏธ ์กด์žฌํ•˜๋Š” JobInstance๋ฅผ ๋ฆฌํ„ด
  • Meta Data Schema์˜ BATCH_JOB_INSTANCE ํ…Œ์ด๋ธ”๊ณผ ๋งคํ•‘

 

 

JobInstance ๊ณผ์ •

(์ฐธ๊ณ : ์ธํ”„๋Ÿฐ Spring Batch)

 

JobLauncher๋Š” ์‹ค์ œ Batch Job์„ ์‹คํ–‰์‹œํ‚ค๋Š” Launcher๋กœ Job์„ ์‹คํ–‰ํ•  ๋•Œ(run) Job, JobParameters ๋‘ ๊ฐœ์˜ ์ธ์ž๋ฅผ ๋ฐ›์•„์„œ Spring Batch Job์„ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค.

 

์œ„ ์‚ฌ์ง„์ฒ˜๋Ÿผ Job = ์ผ๋ณ„ ์ •์‚ฐ, JobParameters = '2021.01.01' ์ด๋ผ๋Š” ์ธ์ž๋ฅผ ๋ฐ›์•„ Job์„ ์‹คํ–‰ํ•œ๋‹ค๊ณ  ํ–ˆ์„ ๋•Œ, JobRepository๋ผ๋Š” ํด๋ž˜์Šค(๋ฉ”ํƒ€ ๋ฐ์ดํ„ฐ์˜ ์ €์žฅ์†Œ ์—ญํ• )๊ฐ€ Job & JobParameters๋ฅผ Key๋กœ ํ•ด์„œ ์ด์ „์— ์‹คํ–‰ํ•œ ๋‚ด์—ญ์ด ์กด์žฌํ•˜๋Š”์ง€ DB์—์„œ ์กฐํšŒํ•ฉ๋‹ˆ๋‹ค.

 

Job & JobParameters๋กœ ์กฐํšŒ ํ›„ JobInstance๊ฐ€ ์กด์žฌํ• ๊ฒฝ์šฐ, ๊ธฐ์กด JobInstance๋ฅผ ๋ฆฌํ„ดํ•˜๊ณ  ์กด์žฌํ•˜์ง€ ์•Š์„ ๊ฒฝ์šฐ ์ƒˆ๋กœ์šด JobInstance๋ฅผ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.

(JobParameters๋Š” Spring Batch๊ฐ€ ์‹คํ–‰๋  ๋•Œ ์™ธ๋ถ€์—์„œ ๋ฐ›๋Š” ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ์˜๋ฏธํ•˜๋Š”๋ฐ์š”, ๋’ค์—์„œ ์ž์„ธํžˆ ์‚ดํŽด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.)

 

 

์˜ˆ์ œ ์ฝ”๋“œ

(์ฝ”๋“œ ํŽธ์˜ ์ƒ import๋Š” ๋ชจ๋‘ ์ œ์™ธํ–ˆ์Šต๋‹ˆ๋‹ค.)

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
@RequiredArgsConstructor
@Configuration
public class JobInstanceConfiguration {
 
    private final JobBuilderFactory jobBuilderFactory;
    private final StepBuilderFactory stepBuilderFactory;
 
    @Bean
    public Job instanceBatchJob() {
        return this.jobBuilderFactory.get("Job")
                .start(instanceStep1())
                .next(instanceStep2())
                .build();
    }
 
    @Bean
    public Step instanceStep1() {
        return stepBuilderFactory.get("instanceStep1")
                .tasklet((contribution, chunkContext) -> {
                    JobInstance jobInstance = contribution.getStepExecution().getJobExecution().getJobInstance();
                    System.out.println("jobInstance.getId() : " + jobInstance.getId());
                    System.out.println("jobInstance.getInstanceId() : " + jobInstance.getInstanceId());
                    System.out.println("jobInstance.getJobName() : " + jobInstance.getJobName());
                    System.out.println("jobInstance.getJobVersion : " + jobInstance.getVersion());
                    return RepeatStatus.FINISHED;
                })
                .build();
    }
 
    @Bean
    public Step instanceStep2() {
        return stepBuilderFactory.get("instanceStep2")
                .tasklet((contribution, chunkContext) -> {
                    System.out.println("step2 has executed");
                    return RepeatStatus.FINISHED;
                })
                .build();
    }
}
cs

ํ•˜๋‚˜์˜ Job๊ณผ 2๊ฐœ์˜ Step์„ ๊ฐ€์ง€๋Š” ๊ฐ„๋‹จํ•œ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ฝ”๋“œ๋ฅผ ์œ„์™€ ๊ฐ™์ด ์ž‘์„ฑํ•ด ์ฃผ์—ˆ์Šต๋‹ˆ๋‹ค.

JobInstance๋Š” StepContribution ๊ฐ์ฒด๋กœ๋ถ€ํ„ฐ ๊ฐ€์ ธ์™€ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

 

@Component
public class JobRunner implements ApplicationRunner {

    @Autowired
    private JobLauncher jobLauncher;

    @Autowired
    private Job job;

    @Override
    public void run(ApplicationArguments args) throws Exception {

        JobParameters jobParameters = new JobParametersBuilder()
                .addString("name", "JuHyun")
                .toJobParameters();

        jobLauncher.run(job, jobParameters);
    }
}

JobParameters ํ™•์ธ์„ ์œ„ํ•ด ์‹ค์ œ JobLauncher๋ฅผ ํ†ตํ•ด ์œ„์—์„œ ์ž‘์„ฑํ•œ Batch Job์„ ์‹คํ–‰์‹œํ‚ค๋Š” ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•ฉ๋‹ˆ๋‹ค.

 

 

spring:
  batch:
    job:
      enabled: false
      
 ---
 spring.batch.job.enabled=false

๊ทธ ํ›„ application.yml ํŒŒ์ผ์— Spring Boot๊ฐ€ ์ž๋™์œผ๋กœ ์‹คํ–‰ํ•˜๋Š” Job ์„ค์ •์„ false๋กœ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.

 

์œ„ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•œ ํ›„ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ์‹คํ–‰ํ•ด๋ณด๋ฉด Job์ด ์ •์ƒ์ ์œผ๋กœ ์‹คํ–‰์ด ๋˜๊ณ  ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์ €์žฅ์ด ๋ฉ๋‹ˆ๋‹ค.

BATCH_JOB_INSTANCE
BATCH_JOB_EXECUTION_PARAMS

 

๋™์ผํ•œ ์ฝ”๋“œ ๋ฐ JobParameters๋กœ ๋‘ ๋ฒˆ์งธ ์‹คํ–‰ํ•˜๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์ด ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ๋ณ€๊ฒฝํ•˜๋Š” ์—๋Ÿฌ์™€ ํ•จ๊ป˜ ๋ฐฐ์น˜ ์‹คํ–‰์ด ์‹คํŒจํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

์—๋Ÿฌ ๋ฌธ๊ตฌ๋ฅผ ํ™•์ธํ•ด ๋ณด๋ฉด JobInstance๊ฐ€ ์ด๋ฏธ ์กด์žฌํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๋ฐœ์ƒํ•œ๋‹ค๊ณ  ๋‚˜์™€์žˆ๊ณ , Job์„ ๋‹ค์‹œ ์‹คํ–‰ํ•˜๊ณ  ์‹ถ์€ ๊ฒฝ์šฐ parameters๋ฅผ ๋ณ€๊ฒฝํ•˜๋ผ๊ณ  ๋‚˜์™€์žˆ๊ธฐ ๋•Œ๋ฌธ์— JobParameters๋ฅผ ๋ณ€๊ฒฝํ•œ ํ›„ ๋‹ค์‹œ ์‹คํ–‰ํ•ด ๋ณด๋ฉด ์ •์ƒ์ ์œผ๋กœ ์„ฑ๊ณตํ•ฉ๋‹ˆ๋‹ค.

 

 

 

๐Ÿ“Œ JobParameters

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

  • Spring Batch์˜ Job์„ ์‹คํ–‰ํ•  ๋•Œ ์™ธ๋ถ€์—์„œ ๋ฐ›๋Š” ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ๊ฐ€์ง„ ๋„๋งค์ธ ๊ฐ์ฒด
  • ํ•˜๋‚˜์˜ Job์— ์กด์žฌํ•  ์ˆ˜ ์žˆ๋Š” ์—ฌ๋Ÿฌ๊ฐœ์˜ JobInstance๋ฅผ ๊ตฌ๋ถ„ํ•˜๊ธฐ ์œ„ํ•œ ์šฉ๋„
  • JobInstance์™€ JobParameters๋Š” 1:1 ๊ด€๊ณ„
  • Meta Data Schema์˜ BATCH_JOB_EXECUTION_PARAM ํ…Œ์ด๋ธ”๊ณผ ๋งคํ•‘

 

์ƒ์„ฑ ๋ฐ ๋ฐ”์ธ๋”ฉ

  • ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์‹คํ–‰ ์‹œ ์ฃผ์ž… : java -jar ~.jar -requestDate=20210101
  • ์ฝ”๋“œ๋กœ ์ƒ์„ฑ : JobParameterBuilder
  • SPEL ์‚ฌ์šฉ : @Value(“#{jobParameter[requestDate]}”) - @JobScope, @StepScope ์„ ์–ธ์ด ํ•„์ˆ˜
    • @JobScope, @StepScope๋Š” Job, Step์ด ์‹คํ–‰๋˜๋Š” ์‹œ์ ์— Bean์ด ์ƒ์„ฑ๋˜๋„๋ก Bean์˜ ์ƒ์„ฑ์‹œ์ ์„ ์ง€์—ฐ์‹œํ‚ค๋Š” ์—ญํ• ์„ ํ•ฉ๋‹ˆ๋‹ค. @Value ์–ด๋…ธํ…Œ์ด์…˜์˜ ๊ฒฝ์šฐ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด ์‹คํ–‰๋  ๋•Œ ๋ฐ”์ธ๋”ฉ์„ ํ•˜๊ธฐ ๋•Œ๋ฌธ์— @JobScope, @Stepcope ์–ด๋…ธํ…Œ์ด์…˜์ด ์กด์žฌํ•˜์ง€ ์•Š์œผ๋ฉด NullPointerException ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค.

 

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

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
@RequiredArgsConstructor
@Configuration
public class JobParameterConfiguration {
 
    private final JobBuilderFactory jobBuilderFactory;
    private final StepBuilderFactory stepBuilderFactory;
 
    @Primary
    @Bean
    public Job BatchJob() {
        return this.jobBuilderFactory.get("JobParameters")
                .start(step1())
                .next(step2())
                .build();
    }
 
    @Bean
    public Step step1() {
        return stepBuilderFactory.get("JobParametersStep1")
                .tasklet((contribution, chunkContext) -> {
                    JobParameters jobParameters = contribution.getStepExecution().getJobParameters();
                    String name = jobParameters.getString("name");
                    Long seq = jobParameters.getLong("seq");
                    Date date = jobParameters.getDate("date");
 
                    System.out.println("===========================");
                    System.out.println("name: " + name);
                    System.out.println("seq: " + seq);
                    System.out.println("date: " + date);
                    System.out.println("===========================");
 
                    Map<String, Object> jobParameters2 = chunkContext.getStepContext().getJobParameters();
                    String name2 = (String) jobParameters2.get("name");
                    long seq2 = (long) jobParameters2.get("seq");
 
                    System.out.println("name: " + name2);
                    System.out.println("seq: " + seq);
 
                    System.out.println("step1 has executed");
                    return RepeatStatus.FINISHED;
                })
                .build();
    }
 
    @Bean
    public Step step2() {
        return stepBuilderFactory.get("JobParametersStep2")
                .tasklet((contribution, chunkContext) -> {
                    System.out.println("step1 has executed");
                    return RepeatStatus.FINISHED;
                })
                .build();
    }
}
cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@Component
public class JobParameterTest implements ApplicationRunner {
 
    @Autowired
    JobLauncher jobLauncher;
 
    @Autowired
    Job job;
 
    @Override
    public void run(ApplicationArguments args) throws Exception {
 
        JobParameters jobParameters = new JobParametersBuilder().addString("name""JuHyuns")
                .addLong("seq", 1L)
                .addDate("date"new Date())
                .toJobParameters();
 
        jobLauncher.run(job, jobParameters);
    }
}
cs

์œ„ ์˜ˆ์ œ ์ฝ”๋“œ์—์„œ contribution์„ ์ด์šฉํ•˜์—ฌ JobParameters๋ฅผ ๊ฐ€์ ธ์˜ค๋Š” ์ฝ”๋“œ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

JobParameters jobParameters = contribution.getStepExecution().getJobParameters();

contribution์€ StepContribution ํด๋ž˜์Šค์ด๊ณ , StepContribution ํด๋ž˜์Šค ๋‚ด๋ถ€์— StepExecution ํ•„๋“œ๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ๋˜ํ•œ StepExecution ํด๋ž˜์Šค ๋‚ด๋ถ€์—๋Š” JobExecution๋ผ๋Š” ํ•„๋“œ๊ฐ€ ์กด์žฌํ•˜๊ณ , ์ตœ์ข…์ ์œผ๋กœ JobExecution ํด๋ž˜์Šค ๋‚ด์— JobParameters ํ•„๋“œ๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ๊ธฐ ๋•Œ๋ฌธ์— JobParameters๋ฅผ ์ฐธ์กฐํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ฆ‰, ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ฐธ์กฐํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

StepContribution -> StepExecution -> JobExecution (JobParameters)

 

๋˜ํ•œ chunkContext๋ฅผ ํ†ตํ•ด์„œ๋„ JobParameters๋ฅผ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

Map<String, Object> jobParameters2 = chunkContext.getStepContext().getJobParameters();

contribution์™€๋Š” ๋‹ค๋ฅด๊ฒŒ ํƒ€์ž…์ด JobParameters์ด ์•„๋‹Œ ์•„๋‹Œ Map ํ˜•์‹์œผ๋กœ ๊ฐ€์ ธ์˜ค๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

๋”ฐ๋ผ์„œ ์ฒซ ๋ฒˆ์งธ ๋ฐฉ์‹์ธ StepContribution ๊ฐ์ฒด๋ฅผ ํ†ตํ•ด JobParameters๋ฅผ ๊ฐ€์ ธ์˜ค๋Š” ๊ฒƒ์ด ๋ณด๋‹ค ์ข‹์€ ๋ฐฉ๋ฒ•์ผ ๋“ฏํ•ฉ๋‹ˆ๋‹ค.

 

 

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

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
@RequiredArgsConstructor
@Configuration
public class JobParameterConfiguration {
 
    private final JobBuilderFactory jobBuilderFactory;
    private final StepBuilderFactory stepBuilderFactory;
 
    @Primary
    @Bean
    public Job batchJob() {
        return this.jobBuilderFactory.get("JobParameters")
                .start(step1(null))
                .next(step2())
                .build();
    }
 
    @Bean
    @JobScope // Scope ์ถ”๊ฐ€(์ง€์—ฐ ์ดˆ๊ธฐํ™”)
    public Step step1(@Value("#{jobParameters[requestDate]}"String requestDate) {
        return stepBuilderFactory.get("JobParametersStep1")
                .tasklet((contribution, chunkContext) -> {
                    System.out.println("=======================");
                    System.out.println("requestDate: " + requestDate);
                    System.out.println("=======================");
                    return RepeatStatus.FINISHED;
                })
                .build();
    }
 
    @Bean
    public Step step2() {
        return stepBuilderFactory.get("JobParametersStep2")
                .tasklet((contribution, chunkContext) -> {
                    System.out.println("step1 has executed");
                    return RepeatStatus.FINISHED;
                })
                .build();
    }
}
 
cs

๊ธฐ๋ณธ์ ์œผ๋กœ ์˜ˆ์ œ ์ฝ”๋“œ 1๊ณผ ๋น„์Šทํ•˜์ง€๋งŒ, SPEL ์ ์šฉ์„ ์œ„ํ•ด step๋ฉ”์„œ๋“œ์— ์ƒ๋‹จ์— @JobScope ์–ด๋…ธํ…Œ์ด์…˜๊ณผ ๋ฉ”์„œ๋“œ ์ธ์ž์— @Value ์–ด๋…ธํ…Œ์ด์…˜์„ ํ†ตํ•ด JobParameters์˜ ์ •๋ณด๋ฅผ ์ถ”๊ฐ€ํ•ด์ฃผ์—ˆ์Šต๋‹ˆ๋‹ค.

 

์ถ”๊ฐ€์ ์œผ๋กœ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์‹คํ–‰ ์‹œ ์ธ์ž๋ฅผ ์ „๋‹ฌํ•˜๊ธฐ ์œ„ํ•ด Edit Configurations > Modify options > Program arguments๋ฅผ ํ†ตํ•ด ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ์ „๋‹ฌํ•œ ํ›„ Spring Batch Job์„ ์‹คํ–‰ํ•ด๋ณด๋ฉด ์ •์ƒ์ ์œผ๋กœ ๊ฐ’์„ ๋ฐ›์•„์˜ฌ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ฐธ๊ณ ๋กœ JobLauncher๊ฐ€ ์•„๋‹Œ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์‹คํ–‰ ์‹œ Batch๋ฅผ ์‹คํ–‰ํ•˜๋ ค๋ฉด application.yml ํŒŒ์ผ์—์„œ ์•„๋ž˜์™€ ๊ฐ™์ด ์„ค์ •์„ ํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค.

spring:
  batch:
    job:
      enabled: true
      names: JobParameters
  profiles:
    active: local

 

JobParameters๋ฅผ ์™ธ๋ถ€์—์„œ ์ „๋‹ฌ๋ฐ›์Œ์œผ๋กœ์จ ์–ป๋Š” ์ด์ ์€ ๊ต‰์žฅํ•œ๋ฐ์š”, ํŠน์ • ๊ฐ’์— ๋”ฐ๋ผ ๋ถ„๊ธฐ ์ฒ˜๋ฆฌ๊ฐ€ ํ•„์š”ํ•œ ๋‚ด๋ถ€ ๋กœ์ง ๋“ฑ์˜ ์•„๋ž˜ ์˜ˆ์‹œ์™€ ๊ฐ™์€ ๊ฒฝ์šฐ ๊ต‰์žฅํžˆ ์œ ์šฉํ•˜๊ฒŒ ํ™œ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

  • ๋‚ ์งœ๋ณ„ ๋ถ„๊ธฐ ์ฒ˜๋ฆฌ๊ฐ€ ํ•„์š”ํ•œ ๊ฒฝ์šฐ
  • Spring Batch Job์˜ ์‹คํ–‰ ์ฃผ์ฒด๊ฐ€ Jenkins๋กœ ๊ด€๋ฆฌ๋  ๊ฒฝ์šฐ, Jenkins ๋นŒ๋“œ ์‹œ ๋™์  ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ๋ฐ›์•„ ์ฒ˜๋ฆฌํ•ด์•ผ ํ•  ๊ฒฝ์šฐ
  • ์ธํ„ฐํŽ˜์ด์Šค์˜ ํƒ€์ž…์— ๋”ฐ๋ผ ๋™์ ์œผ๋กœ ๋กœ์ง, ๊ฒฝ๋กœ ๋“ฑ์ด ๋ณ€๊ฒฝ๋˜์–ด์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ

 

 

 

๐Ÿ“Œ JobExecution

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

  • JobExecution์€ Job์— ๋Œ€ํ•œ ํ•œ ๋ฒˆ์˜ ์‹œ๋„๋ฅผ ์˜๋ฏธํ•˜๋Š” ๊ฐ์ฒด๋กœ Job ์‹คํ–‰ ์ค‘ ๋ฐœ์ƒํ•œ ์ •๋ณด๋“ค์„ ์ €์žฅํ•˜๊ณ  ์žˆ๋Š” ๊ฐ์ฒด
    • ์‹œ์ž‘ ์‹œ๊ฐ, ์ข…๋ฃŒ ์‹œ๊ฐ, ์ƒํƒœ, ์ข…๋ฃŒ ์ƒํƒœ ๋“ฑ์˜ ์ •๋ณด
  • JobInstance์™€์˜ ๊ด€๊ณ„
    • JobExecution์˜ ์‹คํ–‰ ์ƒํƒœ ๊ฒฐ๊ณผ๊ฐ€ 'COMPLETED' -> Job ์žฌ ์‹คํ–‰ ๋ถˆ๊ฐ€๋Šฅ
    • JobExecution์˜ ์‹คํ–‰ ์ƒํƒœ ๊ฒฐ๊ณผ๊ฐ€ 'FAILED' -> Job ์žฌ ์‹คํ–‰ ๊ฐ€๋Šฅ
    • JobExecution์˜ ์‹คํ–‰ ์ƒํƒœ ๊ฒฐ๊ณผ๊ฐ€ 'COMPLETED'๊ฐ€ ๋  ๋•Œ๊นŒ์ง€ ํ•˜๋‚˜์˜ JobInstance ๋‚ด์—์„œ ์—ฌ๋Ÿฌ ๋ฒˆ ์‹œ๋„ ๊ฐ€๋Šฅ
    • JobInstance์™€ JobExecution๋Š” 1 : N์˜ ๊ด€๊ณ„  
  • BATCH_JOB_EXECUTION ํ…Œ์ด๋ธ”๊ณผ ๋งคํ•‘

 

JobExecution ๊ณผ์ •

(์ฐธ๊ณ : ์ธํ”„๋Ÿฐ Spring Batch)

  1. JobLauncher๊ฐ€ Batch Job์„ ์‹คํ–‰(Job & JobParameters)
  2. JobRepository๊ฐ€ Job & JobParameters๋กœ DB ์กฐํšŒ
  3. JobInstance๊ฐ€ ์กด์žฌํ•  ๊ฒฝ์šฐ ๊ธฐ์กด JobInstance๋ฅผ ๋ฆฌํ„ด, ์กด์žฌํ•˜์ง€ ์•Š์„ ๊ฒฝ์šฐ ์ƒˆ๋กœ์šด JobInstance๋ฅผ ๋ฆฌํ„ด
  4. JobInstance๊ฐ€ ์ƒ์„ฑ๋˜๋ฉด JobExecution๋„ ์ƒˆ๋กœ ์ƒ์„ฑ๋จ
  5. ์ด๋ฏธ ์กด์žฌํ•˜๋Š” JobInstance์—์„œ BatchStatus์˜ ์กฐ๊ฑด์— ๋”ฐ๋ผ Job์˜ ์žฌ ์‹คํ–‰ ๊ฐ€๋Šฅ ์—ฌ๋ถ€ ํŒ๋‹จ

 

์˜ˆ์ œ ์ฝ”๋“œ

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
@RequiredArgsConstructor
@Configuration
public class JobExecutionConfiguration {
 
    private final JobBuilderFactory jobBuilderFactory;
    private final StepBuilderFactory stepBuilderFactory;
 
    @Primary
    @Bean
    public Job executionJob() {
        return this.jobBuilderFactory.get("JobExecution")
                .start(executionStep1())
                .next(executionStep2())
                .build();
    }
 
    @Bean
    public Step executionStep1() {
        return stepBuilderFactory.get("JobExecutionStep1")
                .tasklet((contribution, chunkContext) -> {
                    JobExecution jobExecution = contribution.getStepExecution().getJobExecution();
                    System.out.println("jobExecution = " + jobExecution);
 
                    System.out.println("step1 has executed");
                    return RepeatStatus.FINISHED;
                })
                .build();
    }
 
    @Bean
    public Step executionStep2() {
        return stepBuilderFactory.get("JobExecutionStep2")
                .tasklet((contribution, chunkContext) -> {
//                    throw new RuntimeException("JobExecution has failed");
                    System.out.println("step2 has executed");
                    return RepeatStatus.FINISHED;
                })
                .build();
    }
}
cs

์œ„ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜๊ณ  Batch Job์„ ์‹คํ–‰ํ•ด๋ณด๋ฉด ์ •์ƒ์ ์œผ๋กœ ์‹คํ–‰๋˜๊ณ  ์ƒํƒœ๊ฐ€ COMPLETED๋กœ ์ €์žฅ์ด ๋ฉ๋‹ˆ๋‹ค.

 

...

    @Bean
    public Step executionStep2() {
        return stepBuilderFactory.get("JobExecutionStep2")
                .tasklet((contribution, chunkContext) -> {
                    throw new RuntimeException("JobExecution has failed"); // ์˜ˆ์™ธ ๋ฐœ์ƒ
                })
                .build();
    }

์œ„ ์ฝ”๋“œ์ฒ˜๋Ÿผ step์—์„œ ๊ฐ•์ œ๋กœ ์˜ˆ์™ธ๋ฅผ ๋ฐœ์ƒ์‹œ์ผœ ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

 

์ด๋ฒˆ์—๋Š” Batch Job์—์„œ ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•˜์—ฌ ์ƒํƒœ๊ฐ€ FAILED๋กœ ๊ธฐ๋ก์ด ๋ฉ๋‹ˆ๋‹ค.

 

๋งŒ์•ฝ ์œ„ ์ƒํƒœ์—์„œ ๋™์ผํ•œ JobParameters๋กœ ์‹คํ–‰ํ•ด๋ณด๋ฉด ์–ด๋–ป๊ฒŒ ๋ ๊นŒ์š”?

JobInstacne๋Š” ๊ทธ๋Œ€๋กœ์ง€๋งŒ JobExecution๋Š” ๋‹ค๋ฅธ ๋ฐ์ดํ„ฐ๊ฐ€ ์ €์žฅ๋˜๋Š” ๊ฑธ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.


๋”ฐ๋ผ์„œ JobInstance๊ฐ€ ์กด์žฌํ•˜๋”๋ผ๋„, Status๊ฐ€ FAILED์ผ ๊ฒฝ์šฐ ๊ณ„์†ํ•ด์„œ Job์„ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋˜ํ•œ JobInstance์™€ JobExecution์˜ ๊ด€๊ณ„๋Š” 1 : N์ธ ๊ฑธ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

์œ„์—์„œ ๊ฐ•์ œ๋กœ ์˜ˆ์™ธ ๋ฐœ์ƒํ•œ ์ฝ”๋“œ๋ฅผ ์ œ๊ฑฐํ•˜๊ณ  Batch Job์„ ์ •์ƒ์ ์œผ๋กœ ์‹คํ–‰๋˜๋„๋ก ์ˆ˜์ •ํ•˜์—ฌ ํ…Œ์ŠคํŠธํ•ด๋ณด๋ฉด ์ƒํƒœ๊ฐ€ COMPLETED๋กœ ์ €์žฅ์ด ๋˜๊ณ , ์ดํ›„์—๋Š” ๋™์ผํ•œ JobInstance๋กœ Job์„ ์‹คํ–‰ํ•˜๋Š” ๊ฑด ๋ถˆ๊ฐ€๋Šฅํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

 

 

 

์ฐธ๊ณ  ๋ฌธ์„œ

๋ฐ˜์‘ํ˜•

๋Œ“๊ธ€