알이즈웰
[SpringBatch] Job별 Redis class 설정 본문
Spring Batch로 Job을 여러개 구성할 경우 Job별로 Redis connection 여부를 따로 설정하는 것에 대해 삽질했던 내용을 기록하고자 한다.
환경
- Spring Boot version
- 2.2.0.RELEASE
- Spring Batch
- org.springframework.boot:spring-boot-starter-batch
- Redis Dependency
- org.springframework.boot:spring-boot-starter-data-redis
- org.redisson:redisson-spring-boot-starter:3.13.1
목표
- 같은 Batch 프로젝트 내 Job이 2개 이상 있지만 Redis cache 연결은 한 Job만 설정
- Job이 추가될 예정이지만 주로 Redis cache 연결은 하지 않을 것이므로, 기본적인 Job은 Redis Cache 미사용 Job임
-> 그러므로 Redis Cache 사용할 Job에 대해서만 설정하고자 함
코드
AS-IS
BatchApplication
@EnableBatchProcessing
@SpringBootApplication(
scanBasePackages = {...},
exclude = {DataSourceAutoConfiguration.class})
@EnableFeignClients
@EnableCaching
public class BatchApplication {
public static void main(String[] args) {
SpringApplication.run(BatchApplication.class, args);
}
}
Redis cache 사용하는 Job : ABatch > aJob
@Slf4j
@Configuration
@RequiredArgsConstructor
public class ABatch {
//...
private final String JOB_NAME = "aJob";
private final String STEP1_NAME = "alliswellStep";
//RedissonJobExecutionListener : JobExecutionListener 구현 class(afterJob에서 Redisson shutdown 수행)
private final RedissonJobExecutionListener redissonJobExecutionListener;
private final AlliswellWriter alliswellWriter;
@Bean(JOB_NAME)
public Job job() {
return jobBuilderFactory.get(JOB_NAME)
.listener(redissonJobExecutionListener)
.start(alliswellStep())
.build();
}
@Bean
public Step alliswellStep() {
return stepBuilderFactory.get(STEP1_NAME)
.<Entity, Entity>chunk(100)
.reader(alliswellReader())
.writer(alliswellWriter)
.build();
}
//...
}
Redis cache 사용하지 않는 Job : BBatch > bJob
@Slf4j
@RequiredArgsConstructor
@Configuration
public class BBatch {
//...
private final String JOB_NAME = "bJob";
//...
}
aJob에서 호출하는 listener
@Configuration
@RequiredArgsConstructor
public class RedissonJobExecutionListener implements JobExecutionListener {
private final RedissonClient redissonClient;
@Override
public void beforeJob(JobExecution jobExecution) {
}
@Override
public void afterJob(JobExecution jobExecution) {
if (!redissonClient.isShutdown()) {
redissonClient.shutdown();
}
}
}
aJob의 alliswellStep에서 호출하는 writer
@Component
@RequiredArgsConstructor
public class AlliswellWriter implements ItemWriter<Entity> {
//...
}
Redis cache configuration
@Configuration
public class CacheConfig {
//...
}
1차 수정
BatchApplication
-> batch 프로젝트의 모든 application에 Redis Cache 사용을 막음(디폴트는 Redis 안쓰겠다..)
@EnableBatchProcessing
@SpringBootApplication(
scanBasePackages = {...},
// RedissonAutoConfiguration.class를 exclude 추가
exclude = {DataSourceAutoConfiguration.class, RedissonAutoConfiguration.class})
@EnableFeignClients
// @EnableCaching 삭제
public class BatchApplication {
public static void main(String[] args) {
SpringApplication.run(BatchApplication.class, args);
}
}
Redis cache 사용하는 Job : ABatch > aJob
-> job을 명시. 이 job에서는 cache 사용하겠다는 의미로 @EnableCaching 추가
@Slf4j
@Configuration
@RequiredArgsConstructor
//@ConditionalOnProperty로 Job 지정, @EnableCaching로 cache 사용 선언 추가
@ConditionalOnProperty(name = "spring.batch.job.names", havingValue = "aJob")
@EnableCaching
public class ABatch {
//...
}
Redis cache 사용하지 않는 Job : BBatch > bJob
-> job을 명시. Redis Cache 사용하지않겠다는 의미로 @EnableAutoConfiguration exclude 처리(Redis class 제외)
@Slf4j
@RequiredArgsConstructor
@Configuration
//@ConditionalOnProperty로 Job 지정, @EnableAutoConfiguration로 Redis cache class exclude
@ConditionalOnProperty(name = "spring.batch.job.names", havingValue = "bJob")
@EnableAutoConfiguration(exclude = { RedissonAutoConfiguration.class, RedisAutoConfiguration.class })
public class BBatch {
//...
private final String JOB_NAME = "bJob";
//...
}
aJob에서 호출하는 listener
-> aJob에서 수행되는 listener이므로 aJob으로 명시
@Configuration
@RequiredArgsConstructor
//@ConditionalOnProperty로 Job 지정
@ConditionalOnProperty(name = "spring.batch.job.names", havingValue = "aJob")
public class RedissonJobExecutionListener implements JobExecutionListener {
//...
}
aJob의 alliswellStep에서 호출하는 writer
-> aJob에서 수행되는 step의 writer이므로 aJob으로 명시
@Component
@RequiredArgsConstructor
//@ConditionalOnProperty로 Job 지정
@ConditionalOnProperty(name = "spring.batch.job.names", havingValue = "aJob")
public class AlliswellWriter implements ItemWriter<Entity> {
//...
}
Redis cache configuration
-> aJob에서 Cache를 사용할 것이므로 aJob으로 명시
@Configuration
//@ConditionalOnProperty로 Job 지정
@ConditionalOnProperty(name = "spring.batch.job.names", havingValue = "aJob")
public class CacheConfig {
//...
}
문제점
- batch 프로젝트에서 default는 Redis Cache를 사용하지 않는데, redis 미사용 job인 bJob에도 annotation을 추가해야하는 번거로움이 있음
- redis 사용 job인 aJob 하위 클래스들에 과도한 annotation 설정... -> 귀찮..
- 결정적으로.. 오류가 있었다. (하도 annotation을 이것저것 바꿔서 정확히 어떤 상태에서 어떤 에러가 났는지는 기억안나지만)
-> bJob에서 계속 Redis connection을 시도한다던지(컴파일 시 aJob을 같이 말아올리는 느낌)
+ BatchApplication에서 exclude 처리해줘야할 class가 더 있었다..
-> bJob 실행 시 Redis connection 오류.. (아에 연결시도도 하지 말아야하눈디..!!)
최종 수정
BatchApplication
-> RedisAutoConfiguration.class까지 exclude시키지않으면 bJob에서 Redis 연결을 시도한다. 그래서 같이 exclude 해줌
@EnableBatchProcessing
@SpringBootApplication(
scanBasePackages = {...},
exclude = {DataSourceAutoConfiguration.class, RedissonAutoConfiguration.class, RedisAutoConfiguration.class})
@EnableFeignClients
public class BatchApplication {
public static void main(String[] args) {
SpringApplication.run(BatchApplication.class, args);
}
}
Redis cache 사용하는 Job : ABatch > aJob
-> 아래와 같이 import해주지 않으면 BatchApplication에서 exclude했기때문에 class를 못 읽어온다.
-> ABatch가 돌때는 import 해와서 사용할 수 있도록 세팅
@Slf4j
@Configuration
@RequiredArgsConstructor
@Import(value = { RedissonAutoConfiguration.class, RedisAutoConfiguration.class })
@ConditionalOnProperty(name = "spring.batch.job.names", havingValue = "aJob")
@EnableCaching
public class ABatch {
//...
}
Redis cache 사용하지 않는 Job : BBatch > bJob
-> @ConditionalOnProperty과 @EnableAutoConfiguration를 모두 제거
-> 기본적으로 이 배치 프로젝트에서는 Redis를 사용하지않는 Job을 만들것이므로, Redis 사용하지않는 기본 Job들에는 설정을 최소한 아니, 거의 안하게끔 하기 위함(BatchApplication에서 exclude처리한 이유도 이것)
@Slf4j
@RequiredArgsConstructor
@Configuration
public class BBatch {
//...
private final String JOB_NAME = "bJob";
//...
}
aJob에서 호출하는 listener
-> @Configuration -> @Component : Bean 바로 등록하는걸루 하구
-> @ConditionalOnProperty(name = "spring.batch.job.names", havingValue = "aJob") 삭제 : 굳이 명시해주지 않아도 된다.
(대신 @JobScope를 선언해서 Job에 소속되어있는 하위 작업이란 것을 명시한다.)
(하지만.. ABatch에서는 꼭 명시를 해줬어야했다. 아마 Redis 관련 import의 영향이 있는 것 같다.)
@Component
@RequiredArgsConstructor
@JobScope
public class RedissonJobExecutionListener implements JobExecutionListener {
//...
}
aJob의 alliswellStep에서 호출하는 writer
-> @ConditionalOnProperty(name = "spring.batch.job.names", havingValue = "aJob") 삭제
(대신 @StepScope로 Step에 소속되어있는 하위 작업이란 것을 명시한다. 위와 같은 이유로 설정)
@Component
@RequiredArgsConstructor
@StepScope
public class AlliswellWriter implements ItemWriter<Entity> {
//...
}
Redis cache configuration
-> Redis cache configuration은 어차피 Redis를 쓰기위한 설정일 뿐이니 따로 수정할 필요 없다.
@Configuration
public class CacheConfig {
//...
}
소감
이전에 개발했던 spring batch에서는 Job별로 크게 설정이 다른것이 없었는데 이번에 Redis cache라는 것을 적용해보면서 이렇게 영향도가 있을 수 있으므로 체크해야한다는 것을 느꼈다.
처음에는 gradle 설정을 Job별로 다르게 가야하나 싶어서 Job별로 gradle을 다르게 설정하는 방법을 구글링했었는데 찾지 못했다.
(최후의 수단으로 Job을 패키지분리를 하려고 했는데 함께 일하던 분이 그건 아닌것 같다고 하셔서 최대한 다른 방향으로 찾아봤다.)
하루 넘는 시간동안 이 문제때문에 구글링해보고, annotation도 이것저것 써보다가 import를 찾아서 써봤더니 원하는대로 되었다.
함께 고민해주고 조언해주는 동료가 있는게 고맙다.
그리고 또 하나 느낀점은.. Spring 공부 더 하자..^_^..
기록을 위한 용도이고, 쓰임이나 알고있는 내용이 아주 명확하다고 할 수 없으므로 혹시 고쳐야할 점이 있다면 자유롭게 댓글 부탁드립니다.
'Programming > SPRING' 카테고리의 다른 글
property 사용 (0) | 2017.07.18 |
---|