ConcurrentHashMap을 통한 Cache 처리
- ConcurrentHashMap을 활용해서 Spring 내부에서 서버 메모리 기반의 임시 저장소로 개발
- API 실행 시 임시 키 형태의 데이터 저장 및 서비스 마무리 되면 finally에서 key 형태 데이터 삭제
- ConcurrentHashMap은 자바 객체이기 때문에, 프로세스가 살아 있는 동안 메모리에 상주
- 애플리케이션이 실행되고 있는 서버의 메인 메모리(RAM) 에 존재
- 서버 1대에서만 유효하며, 애플리케이션 종료 시 데이터는 모두 삭제
예시
import org.springframework.stereotype.Service;
import java.time.Instant;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
@Service
public class SimpleRequestLockService {
// key: 사용자ID + API명, value: 잠금 만료 시간(Unix epoch millis)
private final Map<String, Long> lockMap = new ConcurrentHashMap<>();
private final long lockDurationMillis = 5000; // 5초 동안 요청 막기
public synchronized boolean tryLock(String key) {
Long expireTime = lockMap.get(key);
long now = Instant.now().toEpochMilli();
if (expireTime == null || expireTime < now) {
// 잠금 없거나 만료됨 → 새로 잠금 설정
lockMap.put(key, now + lockDurationMillis);
return true;
}
// 아직 잠금 유효 → 중복 요청임
return false;
}
public synchronized void releaseLock(String key) {
lockMap.remove(key);
}
}
Guava Cache
- in-memory 캐시 (JVM 내에 존재)
- TTL 지원 (자동 만료 시간 설정 가능)
- 동시성(Thread-safe) 기본 지원
- LRU(Least Recently Used) 제거 정책 지원
- Spring의 @Cacheable 같은 복잡한 설정 없이 사용 가능
예시
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import org.springframework.stereotype.Service;
import java.util.concurrent.TimeUnit;
@Service
public class GuavaLockService {
// TTL 60초
private final Cache<String, Boolean> lockCache = CacheBuilder.newBuilder()
.expireAfterWrite(60, TimeUnit.SECONDS)
.build();
public boolean tryLock(String key) {
// 이미 존재하면 중복 요청
Boolean prev = lockCache.asMap().putIfAbsent(key, true);
return prev == null;
}
public void releaseLock(String key) {
lockCache.invalidate(key);
}
}