먼저 의존성을 추가한다.
// Redisson (Spring Boot 3 호환)
implementation 'org.redisson:redisson-spring-boot-starter:3.36.0'
// 또는 Spring Boot Starter 없이 Redisson만 사용하려면:
// Redisson 코어
implementation 'org.redisson:redisson:3.36.0'
실사용은 별거 없다. 기존 자바의 락 인터페이스를 기반으로 만든거라 비슷한듯.
package net.daum.cafe.ads.domain.shopad.trendpick.woman;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import net.daum.cafe.ads.domain.shopad.dao.ShopAdDaoResponse;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import java.util.concurrent.TimeUnit;
@Service
@Slf4j
@RequiredArgsConstructor
public class TrendPickWomanSchedulingServiceImpl implements TrendPickWomanSchedulingService {
private final RedissonClient redissonClient;
private final TrendPickWomanClient trendPickWomanClient;
private final TrendPickWomanCacheClient trendPickWomanCacheClient;
//TODO: 스케쥴러가 수집할 itemSize를 동적으로 변경할 필요가 있는지?
private static final int DEFAULT_ITEM_SIZE = 6;
//redisson이용한 분산 락 동기화 메서드
//1분마다 스케쥴링 동작
@Scheduled(cron = "0 */1 * * * *", zone = "Asia/Seoul")
public void syncLoadAndSave() {
String lockKey = "trendPickWoman:schedule:lock";
RLock lock = redissonClient.getLock(lockKey);
try {
//waitTime => 0초 대기, 즉 획득 실패하면 대기 없이 즉시 실패로 간주
//leaseTime => 50초가 지나면 강제 락 해제 (다음 배치가 1분 간격임. 건강한 파드에게 기회를 넘겨주는 의도)
boolean isLocked = lock.tryLock(0, 50, TimeUnit.SECONDS);
if (!isLocked) {
log.debug("락 획득 실패, 다른 파드에서 작업중입니다. 스케쥴 실행을 스킵합니다.");
return;
}
try {
log.debug("락 획득 성공, 스케쥴을 실행합니다.");
loadAndSave();
} finally {
lock.unlock();
}
//TODO: 적절한 예외 처리 필요
} catch (InterruptedException ex) {
log.error("락 획득 중 인터럽트 발생: {}", ex.getMessage());
}
}
private void loadAndSave() {
try {
ShopAdDaoResponse shopAdDaoResponse = trendPickWomanClient.requestShopAd(DEFAULT_ITEM_SIZE);
trendPickWomanCacheClient.saveNewShopAd(shopAdDaoResponse);
//TODO: 하위 에러 잡아서 적절히 처리 및 자체 발생 에러 처리 위한 catch 분기 필요
} catch (Exception ex) {
log.error(ex.getMessage(), ex);
}
}
}