티스토리 뷰

BackEnd/Spring MSA

Netflix Zuul의 동작 과정과 Filter 설명

영지는 달리는중 2020. 10. 20. 00:37

이전 포스팅에 Zuul 에 대한 정의와 Zuul 서버 구현에 대하여 알아보았다 

오늘은 더 나아가 Zuul의 동작 원리와 Zuul에서 제공하는 Filter들의 기능에 대하여 정리하려한다

 

Zuul 동작 과정

Zuul은 클라이언트 요청의 많은 트래픽과 다양한 형태의 요청으로 인한 이슈를 신속하고 동적으로 해결하기 위해 groovy언어로 작성된 다양한 형태의 Filter를 실행한다. Filter에 기능을 정의하고, 이슈사항 발생시 적절한 Filter을 추가함으로써 이슈사항을 대비할 수 있다

  • Filter File Manager에서는 일정 주기(정해진 시간) 마다 정해진 directory에서 groovy로 정의된 filter 파일을 가져온다.
  • javax.servlet.http.HttpServlet을 상속받아서 ZuulServlet을 제정의 하였고, request 요청이 들어 올때마다 아래와 같이 preRoute(), route(), postRoute()에서 ZuulFilter Runner를 실행한다.
  • ZuulFilter Runner는 Filter에 정의된 기능을 실행한다.
  • 기본적으로 Filter은 다른 Filter들과 직접적으로 통신할 수 없다. 그래서 각각의 요청별로 RequestContext를 공유(마치 thread local같이)하여 통신 할 수 있다.

Zuul Filter의 종류 

 

PreFilter 요청 전 처리 필터
인증 처리, 헤더에서 토큰을 읽어 유효성 체크
요청 로그, 서비스 제한 등 처리
CustomFilter 추가 처리 필터
RoutingFilter 라우팅 필터
조건에 따라 특정 서버로 전달하고자 할 때 이곳에 로직 구현
Apache httpclient를 사용하여 정해진 Url로 보낼수 있고, Neflix Ribbon을 사용하여 동적으로 라우팅
PostFilter 후 처리 필터
결과 로그, 서비스 모니터링 처리
response에 HTTP header를 추가하거나, response에 대한 응답속도, Status Code, 등 응답에 대한 statistics and metrics을 수집
ErrorFilter 에러 처리 필터
에러에 따른 공통 처리 

Filter Interface

  • String filterType(): 필터 타입에 대한 String 값을 리턴한다. Zuul에서 제공하는 기본 필터타입은 pre,routing,post,error이다. 이 밖에 필터 타입은 GroovyProcessor.runFilters에서 직접 구현해서 추가할 수 있다.
  • int filterOrder(): f같은 Type에서 실행되는 필터들과의 우선순위를 나타내는 int값을 리턴한다.
  • boolean shouldFilter(): 해당 필터가 실행될 것인가에 대한 로직을 담는다. true를 리턴하면 필터가 실행된다.
  • Object run(): run() 메소드는 이 필터가 어떤 작업을 하는지 정의한다.

Filter의 기능

  • Authentication and Security - 클라이언트 요청시 각 리소스에 대한 인증 요구 사항을 식별
  • Insights and Monitoring - 의미있는 데이터 및 통계 제공
  • Dynamic Routing - 필요에 따라 요청을 다른 클러스터(another Zuul server)로 동적 라우팅
  • Load Shedding - 각 유형의 요청에 대해 용량을 할당하고, 초과하는 요청은 제한

Filter 예시

요청에 대한 처리 이전에 Request Method 와 Request URL를 로깅하고 Jwt 토큰 유효성 검사를 하는 prefilter을 구현한 코드이다

@Slf4j
@Component
@RequiredArgsConstructor
public class ZuulPreFilter extends ZuulFilter {

    private final JwtService jwtService;

    @Override
    public String filterType() {
        return "pre";
    }

    @Override
    public int filterOrder() {
        return 1;
    }

    @Override
    public boolean shouldFilter() {
        return true;
    }

    @Override
    public Object run() throws ZuulException {
        RequestContext ctx = RequestContext.getCurrentContext();
        HttpServletRequest request = ctx.getRequest();

        log.info("Request Method : " + request.getMethod());
        log.info("Request URL : " + request.getRequestURL().toString());
        String accessToken = request.getHeader("AccessToken");
        if (!jwtService.validateToken(accessToken)) {
            ctx.setSendZuulResponse(false);
            ctx.setResponseBody("AccessToken is Invalid");
            ctx.getResponse().setHeader("Content-Type", "text/plain;charset=UTF-8");
            ctx.setResponseStatusCode(HttpStatus.UNAUTHORIZED.value());
        }
        return null;
    }

}

참고 블로그

공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2025/01   »
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
글 보관함