天天看點

spring-cloud-gateway(1)--->spring-cloud-gateway的基本使用

1、API網關

     API網關是一個伺服器,是系統的唯一入口。從面向對象設計的角度看,它與外觀模式類似。API網關封裝了系統内部架構,為每個用戶端提供一個定制的API。它可能還具有其它職責,如身份驗證、監控、負載均衡、緩存、請求分片與管理、靜态響應處理。API網關方式的核心要點是,所有的用戶端和消費端都通過統一的網關接入微服務,在網關層處理所有的非業務功能。通常,網關也是提供REST/HTTP的通路API。

網關應當具備以下功能:

  • 性能:API高可用,負載均衡,容錯機制。
  • 安全:權限身份認證、脫敏,流量清洗,後端簽名(保證全鍊路可信調用),黑名單(非法調用的限制)。
  • 日志:日志記錄(spainid,traceid)一旦涉及分布式,全鍊路跟蹤必不可少。
  • 緩存:資料緩存。
  • 監控:記錄請求響應資料,api耗時分析,性能監控。
  • 限流:流量控制,錯峰流控,可以定義多種限流規則。
  • 灰階:線上灰階部署,可以減小風險。
  • 路由:動态路由規則。

目前,比較流行的網關有:Nginx 、 Kong 、Orange等等,還有微服務網關Zuul 、Spring Cloud Gateway等等

對于 API Gateway,常見的選型有基于 Openresty 的 Kong、基于 Go 的 Tyk 和基于 Java 的 Zuul。這三個選型本身沒有什麼明顯的差別,主要還是看技術棧是否能滿足快速應用和二次開發。

2、spring-cloud-gateway 模型圖:

spring-cloud-gateway(1)--->spring-cloud-gateway的基本使用

             在使用spring-cloud-gateway的時候,我們一般的做法是建構一個網關微服務,然後在裡面配置各種路由資訊。

3、實戰案例:

       3.1、建構一個spring-boot項目引入如下依賴:

<dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-gateway</artifactId>
        </dependency>
           

        3.2、項目啟動類:

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

      }
           

         3.3、配置路由:

                 我們配置一個簡單的路由,主要是通過網關服務進行轉發到我們的訂單服務:

spring:
  cloud:
    gateway:
      routes:
      - id: orderapi                    #目前路由的id
        uri: http://localhost:7070      #目前路由轉發的目标服務
        predicates:                     #目前路由的斷言清單
        - Path=/orderapi/**                使用spring-cloud-gateway提供的Path斷言
        filters:                        #目前路由的過濾器清單
        - StripPrefix=1  #表示在路徑比對的時候會去掉第一級,例如輸入http://localhost:6060/orderapi/order/findAll
                         #将會轉發到http://localhost:7070/order/findAll
                         #StripPrefix=1的含義就是會去掉/orderapi  如果等于2就會去掉/orderapi/order
           

         這就是一個簡單的路由配置。在spring-cloud-gateway中提供了很多的斷言、過濾器的實作,詳細可見官網。

4、自定義斷言:

      在spring-cloud-gateway中提供了很多的斷言、過濾器的實作,我們也可以實作自己的斷言或者過濾器,接下來我們以實作斷言為案例進行講解。

      我們實作一個斷言,這個斷言的核心業務是需要檢查請求的Header中是否存在accesstoken這個屬性,如果存在我們進行請求轉發,如果不存在就不進行轉發。

       實作的方式也比較簡單,隻需要實作 AbstractRoutePredicateFactory 這個接口即可,隻是有一下限制而已,固定為斷言Name + "RoutePredicateFactory"字元,然後配置使用的時候使用斷言名稱配置即可。

/**
 * 自定義斷言,類名格式斷言名稱+ “RoutePredicateFactory”
 */
@Component
public class AuthRoutePredicateFactory extends AbstractRoutePredicateFactory<AuthRoutePredicateFactory.Config> {
    public AuthRoutePredicateFactory() {
        super(Config.class);
    }

    //定義配置值的順序
    @Override
    public List <String> shortcutFieldOrder() {
        return Arrays.asList("headeKey");
    }

    @Override
    public Predicate<ServerWebExchange> apply(Config config) {
        return new GatewayPredicate() {
            @Override
            public boolean test(ServerWebExchange serverWebExchange) {
                HttpHeaders headers = serverWebExchange.getRequest().getHeaders();
                List <String> strings = headers.get(config.getHeadeKey());
                if (CollectionUtils.isEmpty(strings)){
                    return false;
                }else {
                    return true;
                }
            }
        };
    }

    @Validated
    public static class Config {

        @NotEmpty
        private String headeKey;

        public Config() {
        }

        public String getHeadeKey() {
            return headeKey;
        }

        public void setHeadeKey(String headeKey) {
            this.headeKey = headeKey;
        }
    }
}
           

       自定義斷言的配置方式:

spring:
  cloud:
    gateway:
      routes:     
      - id: costomer_predicate
        uri: http://www.baidu.com
        predicates:
        - Path=/customer/predicate/**
        - Auth=accesstoken     //自定義的斷言名稱,斷言中的配置執行個體的headeKey=accesstoken
        filters:
        - StripPrefix=2
           

5、網關層使用hystrix進行斷路保護

     5.1、引入hystrix的如下依賴:

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

   5.2、使用spring-cloud-gateway提供的Hystrix相關的過濾器,配置如下:

spring:
  cloud:
    gateway:
      routes:
#正常斷言的使用
      - id: orderapi
        uri: http://localhost:7070
        predicates:
        - Path=/orderapi/**
        filters:
        - StripPrefix=1  #表示在路徑比對的時候會去掉第一級,例如輸入http://localhost:6060/orderapi/order/findAll
                         #将會轉發到http://localhost:7070/order/findAll
                         #StripPrefix=1的含義就是會去掉/orderapi  如果等于2就會去掉/orderapi/order

        配置hystrix過濾器進行斷路保護            
        - name: Hystrix
          args:
            name: fallbackcmd       配置建構的hystrixCommand的id=fallbackcmd
            fallbackUri: forward:http://localhost:6060/fallback
            #fallbackUri: forward:/orderapi/order/mock  #轉發請求到http://localhost:6060//orderapi/order/mock
                                                         #是以也會調到http://localhost:7070/order/mock, 也可以在
                                                         #網關服務中定義controller 進行轉發過去。


可對id=fallbackcmd進行屬性配置
hystrix:
    command:
        fallbackcmd:
            execution:
               isolation:
                  thread:
                     timeoutInMilliseconds: 1000
           

6、網關層使用redis進行網關層的限流

      6.1、先引入redis相關依賴如下:

<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis-reactive</artifactId>
        </dependency>
           

       6.2、實作限流的key解析器,作用就是定義限流的規則,例如按照請求參數中的某個值進行限流,我們就以請求中的userId進行限流來實作一個key解析器:

@Bean
    KeyResolver userKeyResolver() {
        return exchange -> Mono.just(exchange.getRequest().getQueryParams().getFirst("userId"));
    }
           

       6.3、配置redis:   

spring:  
   redis:
    host: localhost
    port: 6379
           

      6.4、給某個路由配置限流:

spring:
  application:
    name: gateway-server
  cloud:
    gateway:
      routes:
#正常斷言的使用
      - id: orderapi
        uri: http://localhost:7070
        predicates:
        - Path=/orderapi/**
        filters:
        - StripPrefix=1  #表示在路徑比對的時候會去掉第一級,例如輸入http://localhost:6060/orderapi/order/findAll
                         #将會轉發到http://localhost:7070/order/findAll
                         #StripPrefix=1的含義就是會去掉/orderapi  如果等于2就會去掉/orderapi/order
        - name: Hystrix
          args:
            name: fallbackcmd
            fallbackUri: forward:http://localhost:6060/fallback
#            fallbackUri: forward:/orderapi/order/mock  #轉發請求到http://localhost:6060//orderapi/order/mock
                                                         #是以也會調到http://localhost:7070/order/mock, 也可以在
                                                         #網關服務中定義controller 進行轉發過去。
        - name: RequestRateLimiter
          args:
            # redis-rate-limiter是基于令牌桶來實作的
            #rate-limiter: "#{@redisRateLimiter}"       #指定限流器的beanName, 如果我們配置了redis-rate-limiter.*
                                                         #redis-rate-limiter: "#{@redisRateLimiter}"  redisRateLimiter 這個bean也是
                                                         #spring-cloud-gateway自動裝配的,當然如果我們自定義限流器的話,我們是需要配置
                                                         #自定義的限流器beanName的。
            redis-rate-limiter.replenishRate: 10    #往令牌桶裡放令牌的速率,3 表示每秒放3個令牌進去
            redis-rate-limiter.burstCapacity: 20    #令牌通中最多放多少個令牌,這個值就約等于每秒最大請求數。
            redis-rate-limiter.requestedTokens: 1  #每次請求消耗多少個令牌
            key-resolver: "#{@userKeyResolver}"        #限流鍵的解析器,此處配置的是實作了KeyResolver的bean userKeyResolver,
                                                            #例如userKeyResolver從請求中擷取有查詢參數的user的入參,例如 user=1
                                                            #userId=1  跟userId=2 的令牌通是隔離的,不通用的。
           

7、整合服務注冊與發現在轉發服務按照服務名稱進行負載均衡的配置:

      我們部署微服務的時候基本上都是叢集部署,并且将服務資訊注冊到服務注冊中心,那麼在網關層面就需要進行服務發現,然後進行轉發的負載均衡。

     7.1、在網關服務中映入eureka-clientd 的相關依賴:

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

     7.2、配置eureka-server資訊:

eureka:
  client:
    service-url:
      defaultZone: http://localhost:9090/eureka
           

    7.3、配置激活器網關的服務發現定位器:

spring:
  cloud:
    gateway:
      discovery:
        locator:
          enabled: true
          lower-case-service-id: true
           

     7.4、配置路由route的uri使其能夠進行負載均衡:

spring:
  application:
    name: gateway-server
  cloud:
    gateway:
      routes:
      - id: userapi
        uri: lb://user-service    //使用lb://服務名稱的方式
        predicates:
        - Path=/userapi/**
        filters:
        - StripPrefix=1
           

8、自定義全局過濾器

      在spring-cloud-gateway中有兩種類型的filter,一種的局部的filter,隻有配置到路由中的時候才生效,還有就是全局的filter,全局的不需要單獨配置給某個route,而是所有的route都生效,接下來我們來實作一個全局的filter,這個filter的業務就是請求前後進行日志輸出:這裡的實作是跟reactor的知識點有關,需要有一定的響應式程式設計的基礎知識。

/**
 * 全局過濾器是不需單獨給predicate 進行配置,因為預設就是給所有的predicate配置上。
 */
@Component
public class CustomerLogGlobalFilter implements GlobalFilter {
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        System.out.println("【CustomerLogGlobalFilter】 pre log 。 。 。");
        Mono <Void> mono = chain.filter(exchange).then(Mono.fromRunnable(() -> {
            MultiValueMap <String, HttpCookie> cookies = exchange.getRequest().getCookies();

            System.out.println("【CustomerLogGlobalFilter】 post log 。 。 。" + cookies.toSingleValueMap());
        }));
        return mono;
    }
}
           

繼續閱讀