과부하 방지를 위해, 게시글 조회수를 구현 할 당시 쿠키를 통해 조회수 증가를 막는 API를 만들었다. 최근 대용량 트래픽 처리에 관심이 생겨, 우선 내가 할 수 있는 일을 찾았다.
그 결과, 꼭 조회수가 아니더라도 쿼리문이 계속해서 발동하기 전에 막을 수 있다면 트래픽 부분에 도움을 줄 수 있다 생각했다.
너는 몇 초 안에 n번의 API가 발동하면 몇 초 동안 차단할꺼니?
public static final long TIME_WINDOW_MILLISECONDS = 2000; // 2초 동안
public static final int MAX_REQUESTS = 4; //4번의 API가 발동하면
public static final long BLOCKING_TIME_MILLISECONDS = 5000; // 5초 동안 너를 막겠다!
private final ConcurrentHashMap<String, AtomicInteger> requestCountMap = new ConcurrentHashMap<>();
각 IP 주소에 대한 요청 횟수
private final ConcurrentHashMap<String, Long> blockedIpMap = new ConcurrentHashMap<>();
차단된 IP 및 종료시간
이렇게 사용 될 상수와 변수들을 생성해 줍니다.
실제 API 요청 처리 전에 호출되는 메서드를 만들어야 합니다.
IP 주소가 차단되었는지, 그렇지 않으면 해당 IP의 요청 횟수를 증가시킵니다.
이제 우리가 허용한 요청 횟수를 초과하면 IP가 시켜야 합니다.
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler) throws Exception {
String ipAddress = getClientIP(request);
if (isIpBlocked(ipAddress)) {
response.setStatus(HttpServletResponse.SC_FORBIDDEN);
response.getWriter().write("IP is temporarily blocked. Please try again later.");
return false;
}
AtomicInteger requestCount = requestCountMap.computeIfAbsent(ipAddress, k -> new AtomicInteger(0));
if (requestCount.incrementAndGet() > WebConstants.MAX_REQUESTS) {
blockIp(ipAddress);
response.setStatus(HttpServletResponse.SC_FORBIDDEN);
response.getWriter().write("Too many requests. IP blocked for a short duration.");
return false;
}
resetRequestCountAfterTimeWindow(ipAddress);
return true;
}