ItemReaderSCAN 명령을 사용하는 일종의 커서 기반 ItemReaderRedisItemReader는 다른 커서 기반 ItemReader와는 다르다.
SCAN 명령어는 조회 대상key 목록들만 반환한다.GET 명령이 필요
SCAN명령어는STRING타입 전용 명령어이기에RedisItemReader는STRING타입만 지원한다. 별도 타입을 처리하려면 커스텀ItemReader를 구현해야 한다.
open())에 SCAN 명령을 최초 호출해 커서 생성read()
read() 메서드는 커서로부터 key를 하나씩 받아와서 GET 명령을 호출SCAN
GET 명령어를 호출하는 구조라 청크 크기가 클 수록 부하가 늘어난다.GET이 몰리면 서버에 상당한 부담이 갈 수도 있다.SCAN 명령이 중복된 key를 반환할 수 있다.RedisItemReader는 반드시 멱등성이 보장된 잡에서만 사용해야 한다.SCAN은 데이터 순서를 보장하지 않는다.RedisItemReader
│
├────── RedisTemplate
│ └─ (Redis 작업을 수행하는 핵심 컴포넌트)
│
├────── Cursor
│ └─ (SCAN 명령으로 생성된 Redis 커서)
│
└────── ScanOptions
└─ (SCAN 명령 수행 시 사용할 옵션들)
RedisTemplate
GET 명령과 초기 커서 생성을 수행Cursor
SCAN 명령의 결과물ScanOptions
@Bean
fun attackLogReader(): RedisItemReader<String, AttackLog> =
RedisItemReaderBuilder<String, AttackLog>()
.redisTemplate(redisTemplate)
.scanOptions(
ScanOptions
.scanOptions()
.match("attack:*") // attack: 으로 시작하는 키만 스캔
.count(10)
.build(),
).build()
RedisItemReaderBuilder
redisTemplate() - RedisTemplate 전달scanOptions() - ScanOptions 전달
count() 메서드 옵션은 엄격한 제한이 아니라 힌트이기에 요청된 개수만큼 반환된다는 보장은 없다.RedisItemReader.read() 구현이다.
@Override
public V read() throws Exception {
if (this.cursor.hasNext()) {
K nextKey = this.cursor.next();
return this.redisTemplate.opsForValue().get(nextKey);
}
else {
return null;
}
}
Pair<K, V> 타입을 반환하여 key, value를 함께 활용할 수 있다면 유용할 것이다.SpringBatch 6에 이 항목이 반영될 수도 있으니 기대해 보자.
RedisItemWriter
RedisTemplate을 활용SET)하거나 삭제(DEL)한다.RedisItemReader처럼 STRING 타입만 지원한다.RedisItemWriter의 itemKeyMapper
Converter<V, K> 타입의 필드V)로 Redis key(K)를 추출하는 역할을 한다.@Bean
fun deleteAttackLogWriter(): RedisItemWriter<String, AttackLog> =
RedisItemWriterBuilder<String, AttackLog>()
.redisTemplate(redisTemplate)
.itemKeyMapper { attackLog: AttackLog -> "attack:" + attackLog.id }
.delete(true)
.build()
itemKeyMapper
delete(true)
RedisItemWriter를 삭제 모드로 동작시킨다.itemKeyMapper에서 추출한 키로 DEL 명령을 실행false로 설정하면 반대로 추출한 키로 값(AttackLog 객체)을 redis에 저장한다.