SpringCloud- Gateway


Gateway


Gatewayは非同期機能も提供しています.Zuulは2.4以降でのみ使用可能です.
Lombok, Gateway, Eureka Discovery Client
  • 設定
  • 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/**
            - id: second-service
              uri: http://localhost:8082/
              predicates:
                - Path=/second-service/**
  • first-service, second-service
  • @RequestMapping("/") 을 아래와 같이 수정
    @RequestMapping("/first-service")
    上記の変更は、Gatewayが呼び出し後に呼び出されるように設定されているため、上記の変更が必要である.

    フィルタの適用

  • クライアント要求
  • ディクショナリ条件(名前)
  • を確認します.
  • プリフィルタ
  • 実行
  • 後フィルタ
  • 応答
  • クライアント
  • 1.Java設定フィルタを使用する

  • yml構成
  • ...
    
    spring:
      application:
        name: apigateway-service
    #  cloud:
    #    gateway:
    #      routes:
    #        - id: first-service
    #          uri: http://localhost:8081/
    #          predicates:
    #            - Path=/first-service/**
    #        - id: second-service
    #          uri: http://localhost:8082/
    #          predicates:
    #            - Path=/second-service/**
  • フィルタ設定
  • package com.example.apiatewayservice.config;
    
    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 {
    
      /**
       * RouteLocator를 반환해야 application.yml에서 설정한 것처럼 라우터 역할을 함
       * RouteLocatorBuilder 인자로 받아 빌더를 라우트 설정 후 리턴
       *
       * 각각의 route path와 uri로 라우트 시키며 중간 filter를 거침
       * first-request 헤더에 first-request-header값을
       * first-response 헤더에 first-response-header값을 넣는다.
       */
      @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();
      }
    }
    
  • first-service,second-service次の方法
  • を追加
      @GetMapping("/message")
      public String message(@RequestHeader("first-request") String header){
        log.info(header);
        return "Hello World in First service";
      }


    上のfirst-serviceのようにsecond-serviceも同じです.

    2.ymlを使用してフィルタを設定する

  • FilterConfig.Javaで@Configuration,@Beanコメント
  • yml構成
  • 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: # 첫번째 키, 두번째 값
                - AddRequestHeader=first-request, first-request-header2
                - AddResponseHeader=first-response, first-response-header2
            - id: second-service
              uri: http://localhost:8082/
              predicates:
                - Path=/second-service/**
              filters:
                - AddRequestHeader=second-request, second-request-header2
                - AddResponseHeader=second-response, second-response-header2

    3.Custom Filterの適用

  • クライアントフィルタ
  • を作成する
    package com.example.apiatewayservice.filter;
    
    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.ServerHttpRequest;
    import org.springframework.http.server.reactive.ServerHttpResponse;
    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 : 웹 플럭스에서 지원함. 비동기 방식에서 사용
          // 내가 reactor 공부한거 참고!! 깃에 있음!
          return chain.filter(exchange).then(Mono.fromRunnable(() -> {
            log.info("Custom POST Filter: response code -> {}", response.getStatusCode());
          }));
        };
      }
    
      public static class Config{
        // config 정보를 집어 넣을 수 있음.
      }
    }
  • yml構成
  • 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: # 첫번째 키, 두번째 값
    #            - AddRequestHeader=first-request, first-request-header2
    #            - AddResponseHeader=first-response, first-response-header2
                - CustomFilter
            - id: second-service
              uri: http://localhost:8082/
              predicates:
                - Path=/second-service/**
              filters:
    #            - AddRequestHeader=second-request, second-request-header2
    #            - AddResponseHeader=second-response, second-response-header2
                - CustomFilter
  • first-service,second-service checkメソッド追加
  •   @GetMapping("/check")
      public String check(){
        return "Hi, there. this is a message from Fist Service";
      }

    4.グローバルフィルタの設定


    サービスグローバルに対応するフィルタを作成できます.
    1.グローバルフィルタの作成
    package com.example.apiatewayservice.filter;
    
    import lombok.Data;
    import lombok.Getter;
    import lombok.Setter;
    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.ServerHttpRequest;
    import org.springframework.http.server.reactive.ServerHttpResponse;
    import org.springframework.stereotype.Component;
    import reactor.core.publisher.Mono;
    
    @Component
    @Slf4j
    public class GlobalFilter extends AbstractGatewayFilterFactory<GlobalFilter.Config> {
    
      public GlobalFilter() {
        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("Global Filter baseMessage: {}", config.getBaseMessage());
    
    
          if(config.isPreLogger()){
            log.info("Global Filter Start: request id -> {}", request.getId());
          }
          return chain.filter(exchange).then(Mono.fromRunnable(() -> {
            if(config.isPostLogger()){
              log.info("Global Filter End: response code -> {}", response.getStatusCode());
            }
          }));
        };
      }
    
      @Getter
      @Setter
      public static class Config{
        // config 정보를 집어 넣을 수 있음.
        private String baseMessage;
        private boolean preLogger;
        private boolean postLogger;
      }
    }
  • yml構成
  • 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: # 첫번째 키, 두번째 값
    #            - AddRequestHeader=first-request, first-request-header2
    #            - AddResponseHeader=first-response, first-response-header2
                - CustomFilter
            - id: second-service
              uri: http://localhost:8082/
              predicates:
                - Path=/second-service/**
              filters:
    #            - AddRequestHeader=second-request, second-request-header2
    #            - AddResponseHeader=second-response, second-response-header2
                - CustomFilter
          default-filters:
            - name: GlobalFilter
              args: # Global Filter 내 inner Class의 Config class 에 값이 들어감.
                baseMessage: Spring Cloud Gateway Global Filter
                preLogger: true
                postLogger: true

    5.ログフィルタの設定

  • LoggingFilter
  • package com.example.apiatewayservice.filter;
    
    import lombok.Getter;
    import lombok.Setter;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.cloud.gateway.filter.GatewayFilter;
    import org.springframework.cloud.gateway.filter.OrderedGatewayFilter;
    import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
    import org.springframework.core.Ordered;
    import org.springframework.http.server.reactive.ServerHttpRequest;
    import org.springframework.http.server.reactive.ServerHttpResponse;
    import org.springframework.stereotype.Component;
    import reactor.core.publisher.Mono;
    
    @Component
    @Slf4j
    public class LoggingFilter extends AbstractGatewayFilterFactory<LoggingFilter.Config> {
    
      public LoggingFilter() {
        super(Config.class);
      }
    
      @Override
      public GatewayFilter apply(Config config) {
        /**
         * webflux에서
         * ServerWebExchange exchange 에 request, response가 있음
         * GatewayFilterChain chain은 다양한 필터 프리 필터,포스트 필터, 프리체인 등
         *
         * OrderedGatewayFilter를 생성하여 우선순위(Order) 설정 가능
         * Ordered.HIGHEST_PRECEDENCE : HIGHEST라서 가장 먼저 실행 가능
         * LOWEST_PRECEDENCE : HIGHEST_PRECEDENCE의 반대
         */
        GatewayFilter filter = new OrderedGatewayFilter((exchange, chain) -> {
          ServerHttpRequest request = exchange.getRequest();
          ServerHttpResponse response = exchange.getResponse();
    
          log.info("Logging Filter baseMessage: {}", config.getBaseMessage());
    
    
          if(config.isPreLogger()){
            log.info("Logging Filter Start: request id -> {}", request.getId());
          }
    
          return chain.filter(exchange).then(Mono.fromRunnable(() -> {
            if(config.isPostLogger()){
              log.info("Logging Filter End: response code -> {}", response.getStatusCode());
            }
          }));
        }, Ordered.HIGHEST_PRECEDENCE);
    
        return filter;
      }
    
      @Getter
      @Setter
      public static class Config{
        // config 정보를 집어 넣을 수 있음.
        private String baseMessage;
        private boolean preLogger;
        private boolean postLogger;
      }
    
    }
  • yml構成
  • 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: # 첫번째 키, 두번째 값
    #            - AddRequestHeader=first-request, first-request-header2
    #            - AddResponseHeader=first-response, first-response-header2
                - name: CustomFilter
                - name: LoggingFilter # 만약에 args를 넣어야한다면 filters 내에 name: 을 앞에 추가해서 구분해줘야함.
                  args:
                    baseMessage: Hi, there
                    preLogger: true
                    postLogger: true
            - id: second-service
              uri: http://localhost:8082/
              predicates:
                - Path=/second-service/**
              filters:
    #            - AddRequestHeader=second-request, second-request-header2
    #            - AddResponseHeader=second-response, second-response-header2
                - CustomFilter
          default-filters:
            - name: GlobalFilter
              args: # Global Filter 내 inner Class의 Config class 에 값이 들어감.
                baseMessage: Spring Cloud Gateway Global Filter
                preLogger: true
                postLogger: true

    Load Banancer-Eureka連動



    1.apigate-service,first-service,second-serviceのpom。xml eureka-クライアントのチェックなし

    <dependency>
      <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
    </dependency>

    2.apiGateway-サービス、first-service、second-serviceの応用。ymlユリカの設定を変更するには:

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

    3.apiGateway-サービスのspring-cloud-ルーティングuriの変更

    spring:
      application:
        name: apigateway-service
      cloud:
        gateway:
          routes:
            - id: first-service
              uri: lb://MY-FIRST-SERVICE
              predicates: # 조건절
                - Path=/first-service/**
              filters: # 첫번째 키, 두번째 값
    #            - AddRequestHeader=first-request, first-request-header2
    #            - AddResponseHeader=first-response, first-response-header2
                - name: CustomFilter
                - name: LoggingFilter # 만약에 args를 넣어야한다면 filters 내에 name: 을 앞에 추가해서 구분해줘야함.
                  args:
                    baseMessage: Hi, there
                    preLogger: true
                    postLogger: true
            - id: second-service
              uri: lb://MY-SECOND-SERVICE
              predicates:
                - Path=/second-service/**
              filters:
    #            - AddRequestHeader=second-request, second-request-header2
    #            - AddResponseHeader=second-response, second-response-header2
                - CustomFilter
    http://localhost:8081/first-serivce/**->lb://MY-FIRST-SERVICE(ユリカでのサービス名)
    http://localhost:8081/->lb://MY-SECOND-SERVICE

    4.first-service,second-service各2個!

  • yml構成
  • 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}}
  • コントローラのチェック方法の変更->ポート数のチェック
  •   @GetMapping("/check")
      public String check(HttpServletRequest request){
        log.info("Server port = {}", request.getServerPort());
    
        return String.format("Hi, there. there is a message from Fist Service on Port %s",
                                environment.getProperty("local.server.port"));
      }

    /checkを呼び出すと、ポートごとに1回繰り返し呼び出されます!