본문 바로가기

개발/Spring Boot

[Spring Boot] Netflix zuul을 이용한 API ByPass

반응형

 

zuul서버는 API Gateway이다
API Gateway는 API의 요청자인 Client(웹어플리케이션 또는 모바일앱)와 API의 제공자인 backend service를 연결하는 중계자

 

API Gateway가 필요한 이유

  • 인증/인가: 부적절한 요청을 차단하여 Backend service를 보호
  • L/B & Routing: Client의 요청에 따라 적절한 backend service를 로드밸런싱하고 연결(라우팅)
  • Logging: 유통되는 트래픽 로깅
  • Circuit Break: Backend service 장애 감지 및 대처

모든 frontend의 요청을 라우팅하므로 다음의 usecase에도 활용할 수 있습니다.

  • 점진적으로 레거시 시스템을 신규 시스템으로 교체: Strangler pattern 적용
  • 트래픽 일부만 새로운 서비스로 라우팅: Canary(카나리) 배포 가능

 

@EnableZuulProxy
@SpringBootApplication
public class MyApplication {
  public static void main(String[] args) {
    SpringApplication.run(MyApplication.class, args);
  }
}

 

package com.hyeon.filter;

import static com.netflix.zuul.context.RequestContext.getCurrentContext;

import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import com.hyeon.property.UrlProperty;
import com.hyeon.property.UrlProperty.Endpoint;
import java.util.Arrays;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;

@Component
public class AccessibleApiFilter extends ZuulFilter {

  @Autowired
  private UrlProperty urlProperty;

  private final AntPathMatcher matcher = new AntPathMatcher();

  @Override
  public Object run() throws ZuulException {
    RequestContext ctx = getCurrentContext();
    ctx.setSendZuulResponse(false);
    ctx.setResponseStatusCode(HttpStatus.NOT_FOUND.value());
    return null;
  }

  // 필터가 동작하는 조건부, true가 return되는 경우 run이 실행됨.
  @Override
  public boolean shouldFilter() {
    RequestContext ctx = getCurrentContext();
    HttpServletRequest request = ctx.getRequest();

    String prefix = urlProperty.getPrefix();
    String apiDocsUrl = prefix + urlProperty.getApiDocsUrl();
    List<Endpoint> endpoints = urlProperty.getEndpoints();

    String uri = request.getRequestURI();
    String method = request.getMethod();

    // api-docs 경로는 filter 대상에서 제외.
    if (apiDocsUrl.equals(uri) && method.equals(HttpMethod.GET.name())) {
      return false;
    }

    // application.yml에서 정의된 허용 API 목록 외에는 404를 return
    return endpoints
      .stream()
      .noneMatch(
        o ->
          matcher.match(prefix + o.getPath(), uri) &&
          Arrays.stream(o.getMethods()).anyMatch(method::equals)
      );
  }

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

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