天天看點

Spring Cloud Gateway:新一代微服務API網關

作者:Java架構嘻嘻嘻
原文:
blog.csdn.net/Cristiano272/article/details/131814765           

1、網關介紹

如果沒有網關,難道不行嗎?功能上是可以的,我們直接調用提供的接口就可以了。那為什麼還需要網關?

因為網關的作用不僅僅是轉發請求而已。我們可以試想一下,如果需要做一個請求認證功能,我們可以接入到 API 服務中。但是倘若後續又有服務需要接入,我們又需要重複接入。這樣我們不僅代碼要重複編寫,而且後期也不利于維護。

由于接入網關後,網關将轉發請求。是以在這一層做請求認證,天然合适。這樣這需要編寫一次代碼,在這一層過濾完畢,再轉發給下面的 API。

是以 API 網關的通常作用是完成一些通用的功能,如請求認證,請求記錄,請求限流,黑白名單判斷等。

API網關是一個伺服器,是系統的唯一入口。

API網關方式的核心要點是,所有的用戶端和消費端都通過統一的網關接入微服務,在網關層處理所有的非業務功能。通常,網關提供REST/HTTP的通路API。

2、Spring Cloud Gateway介紹

Spring Cloud Gateway是Spring Cloud的新一代API網關,基于WebFlux架構實作,它旨在為微服務架構提供一種簡單而有效的統一的API路由管理方式。

Spring Cloud Gateway作為Spring Cloud生态系統中的網關,目标是替代Netflix ZUUL,具有更好的性能、更強的擴充性、以及更豐富的功能特性,其不僅提供統一的路由方式,并且基于Filter鍊的方式提供了網關基本的功能,例如:安全,監控/埋點,限流等。

3、Spring Cloud Gateway的特性

  • 基于Spring Framework 5, Project Reactor和Spring Boot 2.0
  • 動态路由:能夠比對任何請求屬性
  • 可以對路由指定 Predicate 和 Filter
  • 內建Hystrix斷路器
  • 內建Spring Cloud DiscoveryClient 服務發現功能
  • 易于編寫的Predicate和Filter
  • 請求限流
  • 支援路徑重寫

4、Spring Cloud Gateway的三大核心概念

路由(Route): 路由是網關最基礎的部分,路由資訊由一個ID,一個目标URI,一組斷言和過濾器組成。路由斷言Predicate用于比對請求,過濾器Filter用于修改請求和響應。如果斷言為true,則說明請求URI和配置比對,則執行路由。

spring:
  cloud:
    gateway:
      # 定義多個路由
      routes:
      # 一個路由route的id
      - id: path_route
        # 該路由轉發的目标URI
        uri: https://example.org
        # 路由條件集合
        predicates:
        - Path=/test/**
        # 過濾器集合
        filters:
        - AddRequestHeader=X-Request-Id, 1024
        - AddRequestParameter=color, red
           

斷言(Predicate): 參考Java8中的斷言Predicate,用于實作請求比對邏輯,例如比對路徑、請求頭、請求參數等。請求與斷言比對則執行該路由。

過濾器(Filter): 指的是Spring架構中GatewayFilter的執行個體,使用過濾器,可以在請求被路由前後對請求進行修改。

5、Gateway工作流程

用戶端向Spring Cloud Gateway送出請求,然後在Gateway Handler Mapping中找到與請求相比對的路由,将其發送到Gateway Web Handler。Handler再通過指定的過濾器鍊來對請求進行過濾處理,最後發送到我們實際的服務執行業務邏輯,然後傳回。

Spring Cloud Gateway:新一代微服務API網關

過濾器鍊被虛線分隔,是因為過濾器既可以在轉發請求前攔截請求,也可以在請求處理之後對響應進行攔截處理。

6、Gateway核心配置

依賴

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

啟動類

@SpringBootApplication
@EnableEurekaClient
public class GatewayApplication {

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

}
           

application.yml

spring: 
  application:
    name: cloud-gateway 
  cloud:
    gateway:
      routes:
      # 路由的ID,沒有固定規則但要求唯一,建議配合服務名
      - id: config_route
       # 比對後提供服務的路由位址
        uri: http://ityouknow.com
        # 斷言,路徑相比對的條件
        predicates:
        - Path=/routeconfig/rest/**
      - id: header_route
        uri: http://ityouknow.com
        predicates:
        - Header=X-Request-Id, \d+
           

7、動态路由

網關接收外部請求,按照一定的規則,将請求轉發給其他服務或者應用。如果站在服務調用的角度,網關就扮演着服務消費者的角色,此時,如果再來看看服務調用的目标URI配置,就會很自然的發現一個問題,服務提供者調用的位址是寫死的,即網關沒有動态的發現服務,這就涉及到了服務的自動發現問題,以及發現服務後,所涉及到的服務調用的負載均衡的問題。

可以通過Nacos或者Eureka注冊中心動态發現服務,通過Ribbon進行服務調用的負載均衡。同樣,Gateway也可以整合Nacos或者Eureka,Ribbon進而實作動态路由的功能。

想要使用動态路由的功能,首先要整合注冊中心,這裡以Nacos為例

<!--SpringCloud ailibaba nacos -->
<dependency>
 <groupId>com.alibaba.cloud</groupId>
 <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
           
spring:
  application:
    name: cloud-gateway
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848
    gateway:
      routes:
        #路由的ID,沒有固定規則但要求唯一,建議配合服務名
        - id: config_route
  #比對後提供服務的路由位址, 這裡lb之後,跟的是要調用的服務名稱
          uri: lb://nacos-provider-8002
  # 斷言,路徑相比對的條件
          predicates:
            - Path=/routeconfig/rest/**
           

此時,當id為config_route的路由規則比對某個請求後,在調用該請求對應的服務時,就會從nacos注冊中心自動發現服務,并在服務調用的時候實作負載均衡。

8、Predicate

在Gateway中,有一些的内置Predicate Factory,有了這些Pridicate Factory,在運作時,Gateway會 自動根據需要建立其對應的Pridicate對象測試路由條件。

Path 路由斷言 Factory: 根據請求路徑比對的路由條件工廠

spring:
  cloud:
    gateway:
      routes:
      - id: path_route
        uri: https://example.org
        predicates:
        # 如果可以比對的PathPattern有多個,則每個路徑模式以,分開
        - Path=/red/{segment},/blue/{segment}
           

After 路由斷言 Factory: 在指定日期時間之後發生的請求都将被比對

spring:
  cloud:
    gateway:
      routes:
      - id: after_route
        uri: https://example.org
        predicates:
        - After=2017-01-20T17:42:47.789-07:00[America/Denver]
           

Cookie 路由斷言 Factory: Cookie 路由斷言 Factory有兩個參數,cookie名稱和正規表達式。請求包含此cookie名稱且正規表達式為真的将會被比對。

spring:
  cloud:
    gateway:
      routes:
      - id: cookie_route
        uri: https://example.org
        predicates:
        - Cookie=chocolate, ch.p
           

Header 路由斷言 Factory: Header 路由斷言 Factory有兩個參數,header名稱和正規表達式。請求包含此header名稱且正規表達式為真的将會被比對。

spring:
  cloud:
    gateway:
      routes:
      - id: header_route
        uri: https://example.org
        predicates:
        - Header=X-Request-Id, \d+
           

Host 路由斷言 Factory: Host 路由斷言 Factory包括一個參數:host name清單。使用Ant路徑比對規則, . 作為分隔符。

spring:
  cloud:
    gateway:
      routes:
      - id: host_route
        uri: https://example.org
        predicates:
        - Host=**.somehost.org,**.anotherhost.org
           

Method 路由斷言 Factory: Method 路由斷言 Factory隻包含一個參數:需要比對的HTTP請求方式

spring:
  cloud:
    gateway:
      routes:
      - id: method_route
        uri: https://example.org
        predicates:
        - Method=GET
           

自定義Predicate

可以自定義Predicate來實作複雜的路由比對規則:

// 實作自定義 Predicate 工廠
// 通過HostRoutePredicateFactory建立Predicate進行路由判斷
@Component
public class MyHostRoutePredicateFactory extends AbstractRoutePredicateFactory<MyHostRoutePredicateFactory.Config> {

  public MyHostRoutePredicateFactory() {
      // Config 類作為 Predicate 的配置參數類
      super(Config.class);
  }

  public static class Config {
      // 路由比對規則
      private String hostName;

      public String getHostName() {
        return hostName;
      }

      public void setHostName(String hostName) {
        this.hostName = hostName; 
      }
  }

  // 生成一個 Predicate 執行個體
  @Override
  public Predicate<ServerWebExchange> apply(Config config) {
      // 實作比對邏輯
      return exchange -> {
          // 根據config實作比對判斷
          
          String host = exchange.getRequest().getURI().getHost();
          // 比對配置中的域名
          return host.equals(config.getHostName());
      };
  } 

}

// 使用
RouteLocator locator = new RouteLocatorBuilder(router)
  .routes()
  .route("test_route", r -> r.path("/test")
  .filters(f -> f.filter(new MyHostRoutePredicateFactory.Config("www.test.com")))
  .uri("http://localhost:8080"))
  .build();
           

9、自定義Filter

可以通過實作GatewayFilter和Ordered接口自定義Filter來實作請求處理邏輯:

@Component
public class TokenFilter implements GatewayFilter, Ordered {

   @Override
   public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
      //請求處理邏輯
      log.info("請求路徑:"+ exchange.getRequest().getPath());
      
      ServerHttpRequest request = exchange.getRequest();
   MultiValueMap<String, HttpCookie> cookies = request.getCookies();
   List<HttpCookie> tokens = cookies.get("access_token");
   if (tokens == null || tokens.size() == 0) {
      throw new RuntimeException("少了cookie!");
   }
      
      return chain.filter(exchange);
   }

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

}
           

10、預設過濾器

Spring Cloud Gateway内置了多種過濾器,例如:

  • AddRequestHeader GatewayFilter:在請求頭中添加參數
  • PrefixPath GatewayFilter:請求路徑字首
  • Hystrix GatewayFilter: 斷路器
  • RateLimit GatewayFilter: 限流
  • Retry GatewayFilter: 重試