반응형

Spring Cloud Gateway?

  • MSA 환경에서 사용하는 API Gateway중 하나이다
  • API 라우팅, 보안, 모니터링, 메트릭 등의 기능을 간단하고 효과적인 방법으로 제공한다
  • Eureka는 로드 밸런스, Gateway는 라우팅에 초점을 둔 방식이다

API Gateway를 사용하는 이유?

  • 유입되는 모든 요청/응답이 통하기 때문에 인증/보안을 적용하기 좋다
  • URI 따라 서비스 엔드 포인트를 다르게 처리하는 동적 라우팅이 가능해진다
  • 모든 트래픽이 통하기 때문에 모니터링 시스템 구성이 단순해진다
  • 동적 라우팅이 가능하므로 신규 스펙을 서비스 일부에만 적용시키거나 트래픽을 점진적으로 늘려가는 테스트를 수행하기 수월해진다

Spring Cloud Gateway / Spring Cloud Zuul 차이점

  • Spring Cloud Gateway
    • 스프링에서 개발
    • 비동기, 논블로킹 방식으로 처리한다
    • 스프링 기반이기 때문에 스프링에서 사용하기 좋다
    • 성능이 Zuul보다 좋다는 분석이 있다
    • Netty 런타임 기반으로 서블릿 컨테이너나 WAR로 빌드된 경우 동작하지 않는다
  • Spring Cloud Zuul
    • 넷플릭스에서 개발
    • 서블릿 프레임워크 기반으로 개발되어 동기, 블로킹 방식으로 처리
    • Zuul2가 나오면서 비동기, 논블로킹 방식으로도 처리 가능하다

Spring Cloud Gateway 프로젝트 설정하기

pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.7.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.example</groupId>
    <artifactId>spring-msa-api-gateway</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>spring-msa-api-gateway</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>11</java.version>
        <spring-cloud.version>Hoxton.SR9</spring-cloud.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-gateway</artifactId>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
    </dependencies>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>
</project>
application.yml
server:
  port: 8080
---
spring:
  cloud:
    gateway:
      routes:
        - id: user-api
          uri: http://localhost:8081/
          predicates:
            - Path=/api/user/**
#        - id: cafe-api
#          uri: http://localhost:8082/
#          predicates:
#            - Path=/api/cafe/**
user-api 서버에 컨트롤러 추가
  • port : 8081
@Slf4j
@RestController
public class UserController {
    @PostMapping("/api/user/login")
    public String login(
        @RequestHeader(required = false) Map<String, Object> headers,
        @RequestPart(required = false) Map<String, Object> params,
        @RequestBody(required = false) String body
    ) {
        log.info("{}, {}, {}", headers, params, body);
        return "Hello";
    }
}
gateway를 통해 user-api 서버 호출
###
POST http://localhost:8080/api/user/login
Content-Type: application/json

{
    "name": "john"
}
Hello

CustomFilter 추가하기

@Slf4j
@Component
public class CustomFilter extends AbstractGatewayFilterFactory<CustomFilter.Config> {
    public CustomFilter() {
        super(Config.class);
    }

    @Override
    public GatewayFilter apply(Config config) {
        return ((exchange, chain) -> {
            log.info("CustomFilter baseMessage>>>>>>" + config.getBaseMessage());
            if (config.isPreLogger()) {
                log.info("CustomFilter Start>>>>>>" + exchange.getRequest());
            }
            return chain.filter(exchange).then(Mono.fromRunnable(() -> {
                if (config.isPostLogger()) {
                    log.info("CustomFilter End>>>>>>" + exchange.getResponse());
                }
            }));
        });
    }

    @Data
    public static class Config {
        private String baseMessage;
        private boolean preLogger;
        private boolean postLogger;
    }
}
spring:
  cloud:
    gateway:
      default-filters:
        - name: CustomFilter # 글로벌 설정
          args:
            baseMessage: Spring Cloud Gateway CustomFilter1
            preLogger: true
            postLogger: true
      routes:
        - id: user-api
          uri: http://localhost:8081/
          predicates:
            - Path=/api/user/**
          filters:
            - name: CustomFilter # 개별 설정
              args:
                baseMessage: Spring Cloud Gateway CustomFilter2
                preLogger: true
                postLogger: true

After

  • 특정 시간 이후부터 라우팅 처리 (ex. 특정 시간부터 서비스할 이벤트 API)
spring:
  cloud:
    gateway:
      routes:
        - id: user-api
          uri: http://localhost:8081/
          predicates:
            - Path=/api/user/**
            - After=2020-12-15T23:14:00.000+09:00[Asia/Seoul]

Before

  • 특정 시간 이후부터 라우팅 처리하지 않음 (ex. Deprecated API)
spring:
  cloud:
    gateway:
      routes:
        - id: user-api
          uri: http://localhost:8081/
          predicates:
            - Path=/api/user/**
            - Before=2020-12-15T23:14:00.000+09:00[Asia/Seoul]

Between

  • 특정 시간 사이에만 라우팅 처리 (ex. 특정 시간에만 받을 투표 API)
spring:
  cloud:
    gateway:
      routes:
        - id: user-api
          uri: http://localhost:8081/
          predicates:
            - Path=/api/user/**
            - Between=2020-12-01T00:00:00.000+09:00[Asia/Seoul], 2020-12-31T00:00:00.000+09:00[Asia/Seoul]

Weight

spring:
  cloud:
    gateway:
      routes:
        - id: user-api-1
          uri: http://localhost:8081/
          predicates:
            - Path=/api/user/**
            - Weight=group-user-api, 7
        - id: user-api-2
          uri: http://localhost:8082/
          predicates:
            - Path=/api/user/**
            - Weight=group-user-api, 3

RewritePath

  • gateway의 /api/user/login으로 요청하면 user-api의 /api/login으로 라우팅
spring:
  cloud:
    gateway:
      routes:
        - id: user-api
          uri: http://localhost:8081/
          predicates:
            - Path=/api/user/**
          filters:
            - RewritePath=/api/user/(?<path>.*),/api/$\{path}

Retry

spring:
  cloud:
    gateway:
      routes:
        - id: user-api
          uri: http://localhost:8081/
          predicates:
            - Path=/api/user/**
          filters:
            - name: Retry
              args:
                retries: 3 # 재시도 횟수
                statuses: INTERNAL_SERVER_ERROR # 재시도해야하는 HTTP 상태 (org.springframework.http.HttpStatus)
                methods: GET # 재시도해야하는 HTTP 메소드 (org.springframework.http.HttpMethod)
                backoff: # 재시도하는 시간텀 지정
                  firstBackoff: 1000ms
                  maxBackoff: 6000ms
                  factor: 2
                  basedOnPreviousValue: false

Timeout

spring:
  cloud:
    gateway:
      httpclient:
        connect-timeout: 10000 # 10초
        response-timeout: 10s # 10초, 글로벌 설정시 seconds
      routes:
        - id: user-api
          uri: http://localhost:8081/
          predicates:
            - Path=/api/user/**
          metadata:
            connect-timeout: 15000 # 15초
            response-timeout: 15000 # 15초, 개별 설정시 milliseconds

Eureka Load Balance 요청

의존성 추가
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
API Gateway를 EurekaClient로 등록하도록 활성화
@EnableEurekaClient
@SpringBootApplication
public class SpringMsaGatewayApplication {
    public static void main(String[] args) {
        SpringApplication.run(SpringMsaGatewayApplication.class, args);
    }
}
application.yml
eureka:
  client:
    serviceUrl:
      defaultZone: ${EUREKA_URL:http://localhost:8787/eureka/} # 유레카 서버 설정

spring:
  cloud:
    gateway:
      routes:
        - id: user-api
          # uri: http://localhost:8081/ # 기본 방식
          uri: lb://user-api # 유레카를 통한 방식
          predicates:
            - Path=/api/user/**

참고

반응형

'Development > Spring' 카테고리의 다른 글

[Spring] H2 Database  (0) 2021.02.27
[Spring] Spring Cloud Feign  (0) 2020.12.27
[Spring] Spring Cloud Hystrix  (0) 2020.12.27
[Spring] Spring Cloud Eureka  (0) 2020.12.27
[Spring] Spring Cloud Config  (0) 2020.12.27

+ Recent posts