제육's 휘발성 코딩
반응형

Spring Cloud Gateway Filter 적용

image

  • Spring Cloud Gateway는 다음과 같이 마이크로서비스들과 클라이언트단의 라우팅 역할을 해준다. 이때 Filter 기능을 통해 조건을 분기한다. 사전에 정의하는 PreFilter 와 사후에 정의하는 PostFilter가 있다. Filter 정의는 Java 코드 상에서 사용하거나, Property에 정의할 수 있다.

 

동작 과정

image

  • 동작 흐름은 다음과 같이 이루어진다. 최초의 Request시에 Request-Header에 서비스명을 넣어 로직을 처리해주고, 마지막 Response 시에도 마찬가지로 Response-Header에 서비스명을 넣어 요청에 맞는 클라이언트에게 반환주는 방식이다.

 

image

  • 공식문서에는 다음과 같이 정의되어 있다. 필터를 통해 Proxied Service를 기점으로 프록시를 보내기 전인 Pre Filter와 보낸 후인 Post Filter를 구별할 수 있다고한다.

 

필터 적용 - 빈 등록

@RestController
@RequestMapping("/first-service")
@Slf4j
public class FirstServiceController {

  @GetMapping("/welcome")
  public String welcome() {
    return "Welcome to the First Service";
  }

  @GetMapping("/message")
  public String message(@RequestHeader("first-request") String header) {
    log.info(header);
    return "Hello World in First Service. ";
  }
}
  • 서비스 단의 컨트롤러는 다음과 같이 @RequestHeader 를 지정해서, 요청 핸들링을 할 수 있도록 처리하자.

 

import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class FilterConfig {
  @Bean
  public RouteLocator gatewayRoutes(RouteLocatorBuilder builder) {
    return builder.routes()
      .route(r -> r.path("/first-service/**")
             .filters(f -> f.addRequestHeader("first-request", "first-request-header")
                      .addResponseHeader("first-response", "first-response-header"))
             .uri("http://localhost:8081"))
      .route(r -> r.path("/second-service/**")
             .filters(f -> f.addRequestHeader("second-request", "second-request-header")
                      .addResponseHeader("second-response", "second-response-header"))
             .uri("http://localhost:8082"))
      .build();
  }
}
  • RouterLocator 를 빈으로 등록해서 두 개의 서비스를 라우팅 처리해주자.

image

  • 다음과 같이 서비스 요청이 정상 동작하는 것을 볼 수 있다.

 

 <dependency>
   <groupId>io.netty</groupId>
   <artifactId>netty-resolver-dns-native-macos</artifactId>
   <version>4.1.79.Final</version>
   <classifier>osx-aarch_64</classifier>
 </dependency>
  • 만약 M1 맥 환경에서 netty_resolver_dns 에러가 발생한다면 다음과 같이 osx-aarch_64라이브러리를 추가해주자.

 

필터 적용 - 프로퍼티

image

  • 자바 코드 단에서 빈 등록하지 않고, 프로퍼티 설정으로 필터를 적용할 수도 있다.

 

필터 적용 - 커스텀

import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Mono;

@Component
@Slf4j
public class CustomFilter extends AbstractGatewayFilterFactory<CustomFilter.Config> {

  public CustomFilter() {
    super(Config.class);
  }

  @Override
  public GatewayFilter apply(Config config) {
    // Custom Pre Filter
    return ((exchange, chain) -> {
      ServerHttpRequest request = exchange.getRequest();
      ServerHttpResponse response = exchange.getResponse();

      log.info("Custom PRE filter : request id -> {}", request.getId());

      // Custom Post Filter - Mono는 Web Flux 로 Spring 5부터 제공
      return chain.filter(exchange).then(Mono.fromRunnable(() -> {
        log.info("Custom POST filter : response code -> {}", response.getStatusCode());
      }));

    });
  }

  public static class Config {
    // Put the Configuration properties
  }
}
  • AbstractGatewayFilterFactory 를 상속받아서 apply 를 구현하여 커스텀 필터 처리를 할 수 있다.

 

server:
  port: 8000

eureka:
  client:
    register-with-eureka: false
    fetch-registry: false
    service-url:
      defaultZone: http://localhost:8761/eureka

spring:
  application:
    name: apigateway-service

  cloud:
    gateway:
      routes:
        - id: first-service
          uri: http://localhost:8081
          predicates:
            - Path=/first-service/**
          filters:
            - CustomFilter
        - id: second-service
          uri: http://localhost:8082
          predicates:
            - Path=/second-service/**
          filters:
            - CustomFilter
  • application.yml은 다음과 같이 설정한다.

 

Eureka 연동

image

  • Eureka 서버는 다음과 같이 Service Discovery와 마이크로 서비스 등록역할을 한다.
    • 예를들어 FIRST SERVICE에 접근하기 위하여 http://localhost:8000/first-service/welcome 이라는 요청이 오면, API Gateway가 이 요청을 받고 Eureka 서버에 접근한다. 서버를 통해 마이크로서버 정보를 가져온 후 해당 서비스에 요청을 전달한다.

 

application.yml 설정

server:
  port: 8761

spring:
  application:
    name: discoveryservice

eureka:
  client:
    # eureka의 registry에 등록할지 여부 설정
    register-with-eureka: false
    # eureka의 registry에 있는 정보를 가져올지 여부 설정
    fetch-registry: false
  • 유레카 서버는 다음과 같이 설정되어 있다.

 

eureka:
  client:
    register-with-eureka: true
    fetch-registry: true
    service-url:
      defaultZone: http://localhost:8761/eureka
  • 마이크로 서비스들은 유레카 서버에 등록하기 위해 true로 설정해줘야 한다.

 

server:
  port: 8000

eureka:
  client:
    register-with-eureka: true
    fetch-registry: true
    service-url:
      defaultZone: http://localhost:8761/eureka

spring:
  application:
    name: apigateway-service

  cloud:
    gateway:
      default-filters:
        - name: GlobalFilter
          args:
            baseMessage: Spring Cloud Gateway Filter
            preLogger: true
            postLogger: true
      routes:
        - id: first-service
          uri: lb://MY-FIRST-SERVICE
          predicates:
            - Path=/first-service/**
          filters:
            - CustomFilter
        - id: lb://MY-SECOND-SERVICE
          uri: http://localhost:8082
          predicates: 
            - Path=/second-service/**
          filters:
            - name: CustomFilter
            - name: LoggingFilter
              args:
                baseMessage: Hi, there
                preLogger: true
                postLogger: true
  • API Gateway는 다음과 같이 라우팅할 마이크로서비스 정보를 지정하고 있다. 이때 routes 부분을 보면 lb 를 통해 지정된 uri가 보일 것이다. 이는 유레카 서버의 로드 밸런싱 기능을 의미한다.

 

image

  • 유레카 서버를 접속해보면 등록된 서비스 정보를 볼 수 있다. lb://Application명 으로 지정하는 것을 볼 수 있다.

 

server:
  port: 0

spring:
  application:
    name: my-first-service

eureka:
  client:
    register-with-eureka: true
    fetch-registry: true
    service-url:
      defaultZone: http://localhost:8761/eureka
  instance:
    instance-id: ${spring.application.name}:${spring.application.instance_id:${random.value}};
  • 만약 랜덤포트를 통해 각 마이크로서비스를 이중화 시키고 싶을 경우 다음과 같이 port를 0으로 설정하고, instance에서 random value를 설정하면 된다.

 

REFERENCE


https://cloud.spring.io/spring-cloud-gateway/reference/html/

https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-%ED%81%B4%EB%9D%BC%EC%9A%B0%EB%93%9C-%EB%A7%88%EC%9D%B4%ED%81%AC%EB%A1%9C%EC%84%9C%EB%B9%84%EC%8A%A4/unit/68424

반응형
profile

제육's 휘발성 코딩

@sasca37

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요! 맞구독은 언제나 환영입니다^^