각각의 요청마다 조회 수나 노출 수 등을 증가시켜야 한다면 어떻게 처리하는 것이 좋을까?
단순하게는 요청이 올때마다 +1씩 업데이트를 해주면 된다.
하지만 대량의 트래픽이 발생되는 환경에서 매 번 많은 비용이 소모되는 디비 연결과 update가 이루어진다면 부하를 감당하지 못 해 장애로 연결될 것 이 뻔하다.
개선을 위한 방법 중 하나는, 로컬 메모리로 매 번 요청되는 카운트 증가를 관리하고 일정시간마다 한 번 씩 모아서 db에 update하는 방법이다.
참고용으로 코드를 적어둔다.
흐름
-
카운트 증가 요청 시 매번 countMap에 누적 (CountService.addReadCount)
-
매 5초마다 countMap의 누적 카운트를 DB로 업데이트 (ScheduledTasks.addCountInDb)
public class CountService {
public void addReadCount(int pageNo) {
CountHelper countHelper = CountHelper.getInstance();
countHelper.addCount(pageNo);
}
}
public class CountHelper {
private static CountHelper instance;
private ConcurrentHashMap<Integer,AtomicInteger> countMap;
public static CountHelper getInstance() {
if ( instance == null ) {
instance = new CountHelper();
}
return instance;
}
public CountHelper() {
this.countMap = new ConcurrentHashMap<>();
}
public void addCount(Integer mediaNo) {
countMap.putIfAbsent( mediaNo, new AtomicInteger(0) );
countMap.get(mediaNo).incrementAndGet();
}
public boolean isEmpty() {
return this.countMap == null || this.countMap.size() <= 0;
}
public Map<Integer,AtomicInteger> getCountMap() {
return this.countMap;
}
public void init() {
this.countMap.clear();
}
}
@Component
public class ScheduledTasks {
@Autowired private CountRepository countRepository;
@Scheduled(fixedDelay = 5000L)
public void addCountInDb() {
CountHelper countHelper = CountHelper.getInstance();
if ( !countHelper.isEmpty() ) {
Map<Integer, AtomicInteger> countMap = new HashMap<>();
countMap.putAll(countHelper.getCountMap());
countHelper.init();
countRepository.addReadCountNum(countMap);
}
}
}
@Repository
public class CountRepository {
public int addReadCountNum(Map<Integer,AtomicInteger> map) {
int result = 0;
for(Map.Entry<Integer,AtomicInteger> entry : map.entrySet() ) {
CountVo vo = new CountVo( entry.getKey(), entry.getValue().intValue() );
result += update("addReadCountNum", vo );
}
return result;
}
}
이 방법은 CountHelper 인스턴스 생성, countMap의 데이터를 복사하고 초기화하는 순간의 동기화 이슈가 발생할 가능성은 있기때문에 데이터의 누락이 있을 수 있다. 극히 적은 확률이니 단순 통계 용도라면 전혀 무리 없을 듯하나 염두는 해 두시길.
요청에 대해 100% 처리를 보장하는 좀 더 향상된 방법도 다음에 정리해봐야겠다.
'Dev. 자바 > 참고소스' 카테고리의 다른 글
[jackson] json serialize 만 ignore 하고 싶다면? (1) | 2019.01.25 |
---|---|
[jackson] json serialize 시 상위클래스 필드 무시하기 (0) | 2019.01.15 |
[spring - mybatis tip] MyBatis(iBatis)에서 Java Enum code custom typeHandler 사용하기 (1) | 2017.07.06 |
[java 팁] split - pipeline(|)을 구분자로 문자열 쪼갤때 주의 (1) | 2014.06.11 |
[reflection] 클래스(Class) 의 필드명, 값 map으로 가져오기 (0) | 2014.05.08 |