1. 程式人生 > 其它 >微服務之服務閘道器Gateway

微服務之服務閘道器Gateway

一、什麼是Gateway?

  Gateway是Spring生產系統上構建的API服務閘道器,基於Spring5、SpringBoot2和ProjectReactor等技術。它的目標是提供一種簡單有效的方式對API進行路由,以及提供一些強大的過濾器功能,包括熔斷、限流、重試等。

  SpringCloud Gateway是SpringCloud的一個全新專案,基於WebFlux框架實現的,而webFlux底層使用了高效能的Reactor模式通訊框架Netty。SpringCloud Gateway為微服務架構提供一種簡單有效的統一的API路由管理方式,目的是為了替代Zuul成為全新一代的服務閘道器。

  前面提到了服務閘道器,那什麼又是服務閘道器呢?它是一個網路關口、通道,是整個微服務平臺所有請求的統一入口,所有的客戶端和服務消費端都只能通過這個關口來訪問微服務。除了作為統一入口之外,它還可以處理非業務功能,承擔認證授權、訪問控制、路由、負載均衡、日誌、快取、對映、過濾、熔斷、註冊、服務編排、API管理、監控、統計分析等職責。所以選擇一個優秀的服務閘道器對於開發微服務體系系統至關重要。以下是借鑑某夥伴的服務架構圖:

二、Gateway的工作流程

  要理解工作流程,先了解以下三個核心名詞

    1、Route路由:路由是構建閘道器的基本模組,由一系列的斷言和過濾器組成,如果斷言為true則匹配該路由。

    2、predicate斷言:預先設定的規則條件。開發人員可以匹配http請求中的內容,如果請求與斷言相匹配則進行路由匹配。

    3、Filte過濾器:過濾器是GatewayFilter的一個例項,它可以在請求被路由前後完成對請求的修改。Gateway中存在一系列過濾鏈,用於完成不同的處理(請求前可以做許可權校驗、流量監控、日誌輸出、協議轉換等,請求後響應內容修改等)。

  一個web請求進來,Gateway首先會進行條件匹配,定位真正的服務節點,並在轉發web請求的前後進行一些額外的處理,這些處理就通過過濾器來實現,而前面的匹配就是通過斷言來實現。以下是閘道器內部的工作流程圖

三、服務閘道器模組的搭建

  搭建服務閘道器模組的步驟:

  1、引入jar包:除了引入gateway的jar包外,閘道器本身也是一種微服務,也需要註冊到服務註冊中心,因此還需要引入相應的註冊中心客戶端jar包,進行相應的配置(配置方式參考服務註冊中心相關文件)。最後要注意的是,作為閘道器的模組請不要引入

與spring-boot-starter-web相關的包,否則會出現衝突報錯。

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

   2、修改application.yml檔案,配置閘道器路由規則。路由是多個,可以配置在routes節點下,如圖所示表示配置了兩個路由規則: 

server:
  port: 9527

spring:
  application:
     name: cloud-gateway
  cloud:
     gateway:
        routes:
           - id: payment_routh                     #路由的ID,沒有固定規則但要求唯一,建議配合服務名
             uri: http://localhost:8001            #匹配後提供服務的路由地址
             predicates:  
- Path=/payment/get/** #斷言,路徑相匹配的進行路由 - id: payment_routh2 #路由的ID,沒有固定規則但要求唯一,建議配合服務名 uri: http://localhost:8001 #匹配後提供服務的路由地址 predicates:
- Path=/payment/lb/** #斷言,路徑相匹配的進行路由

  當進行上述配置後,我們就可以不直接訪問http://localhost:8001/payment/get/來呼叫微服務,而是通過http://localhost:9527/payment/get/來間接呼叫8001微服務。當客戶端訪問http://localhost:9527/****/***時,閘道器服務模組就根據路由規則進行匹配,從而呼叫不同的微服務(閘道器服務模組獲取到請求地址後,用請求地址去斷言,斷言uri指定的微服務下是否存在predicates指定的路徑模式,如果為true則呼叫)。

  3、完成以上兩步,啟動閘道器服務模組,客戶端向微服務的請求全部通過訪問該服務來實現,到此簡單的閘道器服務模組搭建完成。

  除了2中配置檔案實現路由配置的方式外,還可以通過硬編碼的方式(程式碼中注入RouteLocator的Bean)來實現路由匹配:新建閘道器配置檔案GatewayConfig類,寫入如下類似程式碼即可

@Configuration
public class GatewayConfig
{ @Bean
public RouteLocator customRouteLocator(RouteLocatorBuilder routeLocatorBuilder)
{ RouteLocatorBuilder.Builder routes
= routeLocatorBuilder.routes();
routes.route(
id: "path_route_name", r -> r.path("/aaaa").uri("http://xxx.com/bbbb") /* /aaaa路徑將會被路由到http://xxx.com/bbbb */
).build();
  return routes.build();
}

四、動態路由配置

  預設情況下,Gateway會根據註冊中心註冊的服務列表,以微服務的名稱為路徑建立動態路由進行轉發,實現動態路由功能。開啟動態路由只需要在上述簡單路由的基礎上修改yml配置檔案,開啟動態路由功能即可,要注意建立動態路由時uri要寫成服務名稱的形式,如下所示:

server:
  port: 9527

spring:
  application:
     name: cloud-gateway
  cloud:
     gateway:
discovery:
locator :
enabled: true #開啟從註冊中心動態建立路由的功能,利用微服務名進行路由
routes:
           - id: payment_routh                     #路由的ID,沒有固定規則但要求唯一,建議配合服務名
             uri: http://cloud-server-name         #這裡寫微服務的名稱,不要用地址
             predicates:  
- Path=/payment/get/** #斷言,路徑相匹配的進行路由

五、Gateway過濾器Filter

  過濾器允許以某種方式修改傳入的HTTP請求或返回的HTTP響應。過濾器作用於某些特定路由。Spring Cloud Gateway包括許多內建的 Filter工廠,這些工廠負責建立Filter過濾器例項,同時也支援自定義過濾器。多個過濾器結合使用可以形成過濾器鏈對http請求進行處理。

  1、內建的過濾器:分為單一過濾器GatewayFilter和全域性過濾器GlobalFilter,作用於業務邏輯之前或業務邏輯之後。使用方式與斷言類似,通過配置實現過濾功能,yml配置參考程式碼如下

server:
  port: 9527

spring:
  application:
     name: cloud-gateway
  cloud:
     gateway:
        discovery:
           locator :
             enabled: true 
        routes:
           - id: payment_routh                     
             uri: http://cloud-server-name         
filters:
- AddRequestParameter=X-Request-Id,1024 #過搪器工廠會在匹配的請求頭加上一對請求頭,名稱為X-Request-Id值為1024
predicates:
- Path=/payment/get/**

  2、自定義全域性過濾器:新建MyGatewayFilter類,實現GlobalFilter,Orderd介面,重寫filter方法和getOrder方法,參考程式碼如下

@Component
public class MyGatewayFilter implements GlobalFilter,ordered  /*實現介面*/
{
    @override
     public Mono<Void> filter(ServerwebExchange exchange,GatewayFilterchain chain) /*exchange是請求的上下文,chain表示下一過濾器*/
{
string uname = exchange.getRequest().getQueryParams( ).getFirst( key: "uname");
if(uname == nul1) { exchange.getResponse().setstatuscode(Httpstatus.NOT_ACCEPTABLE);
return exchange.getResponse().setcomplete(); /*如果uname為空,則設定狀態為不被接受,然後返回結果*/ } return chain.filter(exchange); /*繼續執行下一過濾器*/ } @Override public int getorder()
{
return 0; /*設定過濾器順序*/
}
}

  以上程式碼的作用是過濾非法使用者,只需完成以上編碼,無需額外配置即可實現全域性過濾功能。內建的過濾器此處不再詳述。

六、Gateway常用的斷言Predicate

  分析前面的yml配置檔案,發現predicates是複數形式,表明我們可以設定多個斷言,進行更精確的匹配。Gateway提供瞭如下幾種斷言規則:

規則 配置格式 說明
After

- After=2020-02-05T15:10:03.685+08:00[Asia/Shanghai]

匹配在此時間之後的訪問
Before - Before=2020-02-05T15:10:03.685+08:00[Asia/Shanghai] 匹配在此時間之前的訪問
Between - Between=2020-02-05T15:10:03.685+08:00[Asia/Shanghai], 2020-02-05T15:10:03.685+08:00[Asia/Shanghai] 匹配在該時間段的訪問
Cookie - Cookie=username,zhangsan 匹配cookie名稱為zhangsan
Header - Header=X-Request-Id,\d+ 匹配請求頭要有x-Request-Id屬性並且值為整數
Host - Host=**.yy.com 匹配主機名為.yy.com
Method - Method=Get 匹配get請求
Path - Path=/payment/get/** 匹配路徑
Query - Query=username,\d+ 匹配查詢字串存在username並且值為整數
RemoteAddr - RemoteAddr =192.168.0.1/16 匹配Remote Adress為192.168.0.1