티스토리 뷰

이전 게시글(Thread Safety)과 연관된 글로 과부하 로직을 구현하며 고민했던 것에 대한 내용 정리이다.

현재 운영/개발 중인 서버에 보안 로직으로 과부하 제어 로직을 추가하게 됐다.

 

아래는 과부하 로직의 명세이다.

WAS로 들어오는 모든 request를 카운팅한다. 카운팅한 횟수는 response로 빠져나갈 때 차감된다. request가 reponse로 빠져나가지 않으면 count는 계속 누적되고, 특정 임계치 이상이 넘어가면 서버의 과부하 상태를 response로 내보낸다.

 

위 로직을 구현하기 위해 Apache Tomcat의 멀티쓰레드 구조에 대한 대응으로 Atomic Integer 클래스를 이용해 동시성 이슈를 제어하도록 했다.

 

이번 글에서는 과부하 제어 로직이 위치할 부분에 대한 쓰려고 한다. 들어가기 전에, Spring과 Tomcat에서 어떤 식으로 Request를 처리하고 있는지 알아보고 들어가야한다.

1. Tomcat에서의 Request 처리과정

(그림출처) 사용자가 Request를 서버로 요청하면, servlet 컨테이너에서 Request당 하나씩 Thread를 생성하게 된다.(Dispatcher Servlet에서 servlet과 매핑하는 과정에 대해선 또 다음에 작성할 계획이다)

 

Thread에서 Controller/Service가 동작하기 때문에 Request를 관리하기 위해서는 서비스 로직 구현부와는 다른 부분에서 처리해야한다. 이런 처리를 공통 처리라고하는데, Tomcat과 Spring은 공통처리를 위해 몇 가지 기능이 추가되어있다.

 

  1. Filter
  2. AOP
  3. Interceptor

위 세 가지인데, Filter는 WAS인 Tomcat에서 제공하는 기능이고, AOP와 Interceptor는 Spring 내장 기능이다. 위 기능들이 동작하는 부분을 알기 위해서는 Request 처리 과정을 위한 공통처리 부분을 좀 더 세부적으로 들여다 볼 필요가 있다.

2. 공통 처리

위 그림이 공통처리 부를 가장 잘 설명해 주는 그림 같다.

2-1. Filter

필터는 위 그림처럼 Tomcat에 위치하기 때문에 web.xml에 선언해준다. DispatcherServlet 이전 단계에서 처리되기 때문에 request 자체를 수정할 수 있기 때문에 security 관련 작업도 처리한다.

<filter>
	<filter-name>encodingFilter</filter-name>
	<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
	<init-param>
		<param-name>encoding</param-name>
		<param-value>UTF-8</param-value>
	</init-param>
	<init-param>
		<param-name>forceEncoding</param-name>
		<param-value>true</param-value>
	</init-param>
</filter>
<filter-mapping>
	<filter-name>encodingFilter</filter-name>
	<url-pattern>/*</url-pattern>
</filter-mapping>

<url-pattern>으로 특정 url을 지정해두면 패턴과 매칭되는 모든 url이 filter를 거친다.
자바 클래스에서 @Component annotaion으로 특정 Filter를 불러올 수 있고, implement로 추가 구현한다.

아래는 Filter의 오버라이드 매서드이다.

  • init() - 필터 인스턴스 초기화
  • doFilter() - 전/후 처리
  • destroy() - 필터 인스턴스 종료

 

FilterChain 인터페이스를 통해 여러 필터를 거치게 구현할 수 있다.

2-2. Interceptor

단어 표현대로 request가 들어오면 서비스로직에 들어가기전에 가로채서 전처리를 해준다. 세션 및 쿠키 체크 등을 처리해야 할 때 설정해 사용한다.

public class Intercepter extends HandlerInterceptorAdapter{
 
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
            throws Exception {
        // TODO Auto-generated method stub
        return super.preHandle(request, response, handler);
    }
 
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
            ModelAndView modelAndView) throws Exception {
        // TODO Auto-generated method stub
        super.postHandle(request, response, handler, modelAndView);
    }
 
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
            throws Exception {
        // TODO Auto-generated method stub
        super.afterCompletion(request, response, handler, ex);
    }
 
}

servlet-context에 선언되어 사용되어야 하며, Spring MVC에서는 <mvc:mapping path="/**/*" />을 통해 모든 URL이 interceptor를 거치게 만들 수 있다.

 

아래는 Interceptor의 상속 매서드이다.

 

preHandle: 요청이 컨트롤러로 전달되기 전에 사전 처리 작업한다. 인증 및 권한 검사, 요청 로깅 등의 작업을 보통 이곳에서 처리한다. 위 코드에서 jwt 토큰 인증을 preHandle에서 처리했다.

postHandle: 컨트롤러에서 요청을 처리한 후의 작업을 수행한다. 모델 객체에 데이터를 추가하거나 뷰의 선택을 변경하는 등의 작업을 한다.

afterCompletion: 이 메서드는 요청 처리가 완료된 후에 실행되며, 주로 리소스 정리 및 로깅 작업에 사용된다. 데이터베이스 연결 해제, 리소스 해제, 최종 로그 메시지 작성 등의 작업을 처리한다.

 

2-3. AOP(Aspect Oriented Programming)

Tomcat과 Spring의 특정 기능에 가깝지만, AOP는 개발 방법론에 가깝다. 동일 동작을 하는 코드를 Aspect로 지정해 하나로 묶어서 사용하겠다라는 뜻인데, 이 Spring의 AOP 모듈이 전처리/후처리 기능을 제공한다.

 

코드들이 실행되기 전에 가로채서 동작하기 때문에, Filter, Interceptor와 함께 공통처리 부로 묶이는 것 같다.

class A {
 
    method a() {
        AAAA
 
        method a가 하는 일들
 
        BBBB
    }
 
    method b() {
        AAAA
 
        method b가 하는 일들
 
        BBBB
    }
}
 
class B {
    method c() {
        AAAA
 
        method c가 하는 일들
 
        BBBB
    }
}

쉽게 이야기해서, 클래스 A, B의 a,b/c 매서드에서 AAAA, BBBB 동작을 반복 수행하는데 이 동작들을 떼내어 관리하겠다는 것이다.

 

때문에 URL 단위로 관리되지 않고, PointCut이라는 단위로 Aspect를 구현해서 사용한다.

 

AOP의 대표적인 PointCut 어노테이션들이다.

@Before: 대상 메서드의 수행 전
@After: 대상 메서드의 수행 후
@After-returning: 대상 메서드의 정상적인 수행 후
@After-throwing: 예외발생 후
@Around: 대상 메서드의 수행 전/후

 

이 외에도 더 많은 기능들을 제공하지만, 이 게시글의 취지와 맞지 않기 때문에 추후에 또 정리가 필요할 것 같다.

3. 마치며

결론은 서버 과부하 로직은 Interceptor에 추가됐다. request를 손볼 것도 아니었고, request/response를 캐치할 수 있는 preHandle과 postHandle을 제공했기에 그 위치에서, AtomicInteger로 선언된 request 카운트를 값을 변경하도록 했다.

 

사실 WAS가 한대가 아니기 때문에 이처럼 WAS 한대에서 과부하 제어를 하는게 과연 옳은가?에 대한 고민이 조금 더 필요했을 것 같다. WAS 내부에도 3개의 인스턴스들이 있으니 인스턴스 단위의 관리에 치중했다 생각하면 될 것 같다.

 

또, 공통처리 부분을 보면서 ServletDispatcher에 대한 정리가 먼저였어야 했다는 생각이 든다. 그리고 Filter에서 나온 Security도 가벼운 이야기가 아니고 AOP도 미처 담지못한 내용이 너무나도 많다.

 

한번에 정리된 책이 한권 있으면 파편적으로 알고 있는 Spring에 대한 정보와 지식이 잘 묶일 것 같은데 한번 알아봐야겠다.

공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/07   »
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30 31
글 보관함