Gateway網關
1、微服務網關的基本介紹
不同的微服務一般會有不同的網絡地址,客戶端在訪問這些微服務時必須記住幾十甚至幾百個地址,這對于客戶端方來說太復雜也難以維護,如果讓客戶端直接與各個微服務通訊,可能會有很多問題:
- 客戶端會請求多個不同的服務,需要維護不同的請求地址,增加開發難度
- 在某些場景下存在跨域請求的問題
- 加大身份認證的難度,每個微服務需要獨立認證
因此,我們需要一個微服務網關,介于客戶端與服務器之間的中間層。所有的外部請求都會先經過微服務網關,客戶端只需要與網關交互,只知道一個網關地址即可。

網關是介于客戶端和服務器端之間的中間層,所有的外部請求都會先經過網關這一層。也就是說,API 的實現方更多地只需要考慮業務邏輯,而安全、性能、監控可以交由網關來完成,這樣既提高業務靈活性又不缺安全性。
網關的核心功能特性: 請求路由、權限控制、限流。
- 權限控制:網關作為微服務入口,需要校驗用戶是否有請求資格,如果沒有則進行攔截。
- 路由和負載均衡:一切請求都必須先經過gateway,但網關不處理業務,而是根據某種規則,把請求轉發到某個微服務,這個過程叫做路由。當然路由的目標服務有多個時,還需要做負載均衡。
- 限流:當請求流量過高時,在網關中按照下流的微服務能夠接受的速度來放行請求,避免服務壓力過大。
在SpringCloud中網關的實現包括兩種: gateway、zuul。Zuul是基于Servlet的實現,屬于阻塞式編程。而SpringCloudGateway則是基于Spring5中提供的WebFlux,屬于響應式編程的實現,具備更好的性能。
1.1、使用微服務網關的優點
網關具有的職責,如身份驗證、監控、負載均衡、緩存、請求分片與管理、靜態響應處理。當然,最主要的職責還是與“外界聯系”。
使用網關的優點:
- 安全 。只有網關系統對外進行暴露,微服務可以隱藏在內網,通過防火墻保護。
- 易于監控。可以在網關收集監控數據并將其推送到外部系統進行分析。
- 易于認證。可以在網關上進行認證,然后再將請求轉發到后端的微服務,而無須在每個微服務中進行認證。
- 減少了客戶端與各個微服務之間的交互次數。
- 易于統一授權。
微服務網關就是一個系統,通過暴露該微服務網關系統,方便我們進行相關的鑒權,安全控制,日志統一處理,易于監控的相關功能。
1.2、常見的網關實現方式
常見的實現微服務網關的技術有以下:
- Nginx。 使用Nginx的反向代理和負載均衡可實現對api服務器的負載均衡及高可用。
- zuul。Zuul 是 Netflix 出品的一個基于 JVM 路由和服務端的負載均衡器。Netflix開源,功能豐富,使用JAVA開發,易于二次開發;需要運行在web容器中,如Tomcat。但是缺乏管控,無法動態配置;依賴組件較多;處理Http請求依賴的是Web容器,性能不如Nginx;
- spring cloud gateway。gateway 是spring 出品的 基于spring 的網關項目,集成斷路器,路徑重寫,性能比Zuul好。
2、搭建gateway網關服務
創建一個以 springboot 作為模板的 gateway 模塊,引入nacos服務發現和gateway依賴,如下:
<!--nacos服務注冊發現依賴--> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> </dependency> <!--網關gateway依賴--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-gateway</artifactId> </dependency>
然后在配置文件 application.yml 中配置服務基本信息、nacos地址、路由等即可。
路由配置包括:
- 路由id:路由的唯一標示
- 路由目標(uri):路由的目標地址,http代表固定地址,lb代表根據服務名負載均衡
- 路由斷言(predicates):判斷路由的規則
- 路由過濾器(filters):對請求或響應做處理
如下:
server: port: 10010 spring: application: name: gateway cloud: nacos: server-addr: localhost:8848 # Nacos地址 gateway: routes: - id: user-service # 路由標示,必須唯一 # uri: http://127.0.0.1:8081 # 路由的目標地址。也可以通過http的寫成固定地址,但不推薦使用 uri: lb://userservice # 路由的目標地址。lb就是負載均衡的意思,后面跟服務名稱 predicates: # 路由斷言,判斷請求是否符合規則 - Path=/user/** # 路徑斷言,判斷路徑是否是以/user開頭,如果是則符合 - id: order-service uri: lb://orderservice predicates: - Path=/order/**
啟動該服務,可以在 nacos 注冊中心中查看到該服務,如下:

然后外部服務即可直接通過訪問網關來訪問微服務,而無需直接訪問微服務的地址,如下通過直接訪問 http://localhost:10010/order/101 即可訪問到 order-service 的服務,網關會自動將匹配到的路由轉發至對應的服務中,并且會自動實現負載均衡。

2.1、路由斷言工廠(Route Predicate Factory)
我們在配置文件中寫的斷言規則只是字符串,這些字符串會被 Predicate Factory(斷言工廠) 讀取并處理,轉變為路由判斷的條件。例如 Path=/user/** 是按照路徑匹配,這個規則是由rg.springframework.cloud.gateway.handler.predicate.PathRoutePredicateFactory 類來處理的,像這樣的斷言工廠在SpringCloudGateway還有十幾個。
Spring提供了11種基本的Predicate工廠,如下:

示例:
spring: application: name: gateway cloud: nacos: server-addr: localhost:8848 # Nacos地址 gateway: routes: - id: order-service uri: lb://orderservice predicates: - Path=/order/** - After=2022-04-20T15:35:08.721398+08:00[Asia/Shanghai]
上面的路由斷言意思是必須匹配 /order/ 路徑,并且只有在 2022-04-20 日期后才能匹配成功正常訪問,必須同時滿足條件才能正常訪問到服務,否則無法訪問,接口會報 404。
2.2、路由過濾器工廠(GatewayFilter)
GatewayFilter 是網關中提供的一種過濾器,可以對進入網關的請求和微服務返回的響應做處理。

Spring提供了31種不同的路由過濾器工廠。例如:

示例,比如我們通過路由過濾器來配置所有進入某個服務,比如 userservice 服務的請求都自動添加一個請求頭:X-Request-red=blue。只需在配置 gateway 服務的配置文件中添加以下配置即可,如下:
spring: application: name: gateway cloud: nacos: server-addr: localhost:8848 # Nacos地址 gateway: routes: - id: user-service uri: lb://userservice predicates: - Path=/user/** filters: # 過濾器 - AddRequestHeader=X-Request-red, blue # 添加請求頭
由此,所有通過 gateway 網關發往 userservice 的請求都會自動添加 X-Request-red=blue 請求頭,在 userservice 服務中可以通過參數獲取到該請求頭數據。
上面的過濾器配置是針對了某個具體的服務,如果要對所有的路由都生效,則可以將過濾器工廠寫到 default 下。格式如下:
spring: application: name: gateway cloud: nacos: server-addr: localhost:8848 # Nacos地址 gateway: routes: - id: user-service uri: lb://userservice predicates: - Path=/user/** default-filters: # 默認過濾器,會對所有的路由請求都生效 - AddRequestHeader=X-Request-red, blue # 添加請求頭
可參考官網:https://docs.spring.io/spring-cloud-gateway/docs/current/reference/html/#gatewayfilter-factories
2.3、全局過濾器(GlobalFilter)
全局過濾器(GlobalFilter)與 GatewayFilter 的 default-filters 的作用類似,也是處理一切進入網關的請求和微服務響應,對所有路由都生效。區別在于 GatewayFilter 通過配置定義,只能處理一些比較簡單的邏輯,而 GlobalFilter 的邏輯通過代碼實現,可以自定義復雜邏輯。
要想實現全局過濾器需要實現 GlobalFilter 接口:
public interface GlobalFilter { /** * 處理當前請求,有必要的話通過{@link GatewayFilterChain}將請求交給下一個過濾器處理 * * @param exchange 請求上下文,里面可以獲取Request、Response等信息 * @param chain 用來把請求委托給下一個過濾器 * @return {@code Mono<Void>} 返回標示當前過濾器業務結束 */ Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain); }
示例,我們定義一個全局過濾器,攔截所有的請求,判斷請求的參數中是否有 authorization,并且參數值是否為admin,如果是則放行,否則攔截該請求。
我們在 gateway 服務中創建一個類,通過該類實現 GlobalFilter 接口即可,如下:
import org.springframework.cloud.gateway.filter.GatewayFilterChain; import org.springframework.cloud.gateway.filter.GlobalFilter; import org.springframework.core.Ordered; import org.springframework.http.HttpStatus; import org.springframework.http.server.reactive.ServerHttpRequest; import org.springframework.stereotype.Component; import org.springframework.util.MultiValueMap; import org.springframework.web.server.ServerWebExchange; import reactor.core.publisher.Mono; @Order(0) @Component public class AuthorizeFilter implements GlobalFilter{ @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { // 1.獲取請求參數 ServerHttpRequest request = exchange.getRequest(); MultiValueMap<String, String> params = request.getQueryParams(); // 2.獲取參數中的 authorization 參數 String auth = params.getFirst("authorization"); // 3.判斷參數值是否等于 admin if ("admin".equals(auth)) { // 4.是,放行 return chain.filter(exchange); } // 5.否,攔截 // 5.1.設置狀態碼 exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED); //這里設置了HttpStatus.UNAUTHORIZED狀態碼,即401,如果請求的參數不滿足時將會返回401 // 5.2.攔截請求 return exchange.getResponse().setComplete(); } }
2.4、過濾器的執行順序(defaultFilter、路由過濾器、GlobalFilter)
請求在進入網關后會碰到三類過濾器:當前路由的過濾器、DefaultFilter、GlobalFilter。在請求匹配到路由后,會將 當前路由過濾器、DefaultFilter、GlobalFilter,合并到同一個過濾器鏈(集合)中,通過比較它們之間的 order 值大小進行排序,值越小越先執行,以此來依次執行每個過濾器。
- 每一個過濾器都必須指定一個int類型的order值,order值越小,優先級越高,執行順序越靠前。
- GlobalFilter 通過實現 Ordered 接口,或者添加 @Order 注解來指定 order 值;但是路由過濾器和 defaultFilter 的 order 則由 Spring 自動指定,默認是按照聲明順序從1遞增,比如 defaultFilter 下有多個過濾器,則從上至下默認從1開始遞增,如果 filters 下定義了多個過濾器,同樣從上至下默認從1開始遞增
-
當不同類型之間的過濾器的 order 值大小一樣時,會按照 defaultFilter > 路由過濾器 > GlobalFilter 的順序執行。
3、服務網關解決跨域問題
跨域問題:瀏覽器禁止請求的發起者與服務端發生跨域ajax請求,請求被瀏覽器攔截的問題。網關處理跨域采用的是 CORS 方案,只需要簡單配置即可實現,如下:
spring: application: name: gateway cloud: nacos: server-addr: localhost:8848 # Nacos地址 gateway: # gateway的全局跨域請求配置 globalcors: add-to-simple-url-handler-mapping: true # 解決options請求被攔截問題 corsConfigurations: '[/**]': # 匹配的路由 allowedHeaders: "*" # 允許在請求中攜帶的頭信息 allowedOrigins: "*" # 允許哪些網站的跨域請求。也可以通過數組的方式來只指定一些特定的網站 allowCredentials: true # 是否允許攜帶cookie allowedMethods: "*" # 允許的跨域ajax的請求方式。也可以通過數組的方式來只指定一些特定的請求方式 maxAge: 360000 # 跨域檢測的有效期

浙公網安備 33010602011771號