微服務之服務閘道器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 |