在之前的學習中,所有的微服務接口都是對外開放的,這就意味著用戶可以直接訪問,為了保證對外服務的安全性,服務端實現的微服務接口都帶有一定的權限校驗機制,但是由于使用了微服務,就需要每一個服務都進行一個校驗,當校驗邏輯需要修改時,又得修改多個應用,增加了開發負擔,一個解決方式就是引入 API 網關,類似整個微服務架構的門面,所有的外部客戶端都需要經過它來進行調度和過濾,相當于前臺,需要辦理什么業務,經過前臺的確認之后會引導用戶去對應的服務臺
作為網關,具有以下的幾個核心功能:
- 權限控制:對用戶進行權限校驗,如果校驗失敗就進行攔截
- 動態路由:一切請求先經過網關,但網關不處理業務,而是根據某種規則,把請求發送到某個微服務
- 負載均衡:當路由的目標服務有多個時,進行負載均衡
- 限流:當請求流量過高時,按照網關中配置微服務能夠接受的流量進行放行,避免服務壓力過大
Gateway
1. Gateway 的使用
首先需要創建一個單獨的 Gateway 的服務,除了引入 gateway 的依賴之外,還需要引入 nacos 和 loadbalancer 的依賴
<dependencies><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-gateway</artifactId></dependency><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId></dependency><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-loadbalancer</artifactId></dependency>
</dependencies>
然后就需要對網關進行配置:
spring:application:name: gatewaycloud:nacos:discovery:server-addr: 127.0.0.1:8848gateway:routes: #網關路由配置- id: order-service #自定義路由規則iduri: lb://order-service/ #目標服務地址predicates: #路由條件(滿足條件才會被通行)- Path=/order/**
來解釋一下上面的配置:
由于需要用到 Nacos 的服務發現功能,所以要把 Nacos 的配置也添加上,之后就是網關的路由配置,自定義一個路由規則 id 作為唯一標識符,方便后續對該規則進行管理和引用,然后就是目標服務地址, lb 表示使用負載均衡器將請求轉發到名為 order-service 的服務,并設置了路由條件,請求的路徑必須以 /order/ 開頭
配置好之后啟動服務,此時就可以通過網關服務來獲取資源了
關于多個服務和多個路由條件的配置:
routes: #網關路由配置- id: order-service #自定義路由規則iduri: lb://order-service/ #目標服務地址predicates: #路由條件(滿足條件才會被通行)- Path=/order/**,/feign/**- id: product-serviceuri: lb://product-servicepredicates:- Path=/product/**
2. Route Predicate Factories
在上面的配置中,使用 predicates 來配置路由條件,其中寫的規則只是字符串形式,這些字符串會被 Route Predicate Factory (路由斷言工廠,也稱為路由謂詞工廠)讀取并處理,轉變為路由判斷條件
https://docs.spring.io/spring-cloud-gateway/reference/spring-cloudgateway/request-predicates-factories.html
也就是這 12 種基本的實現:
來演示一下 After 的使用,如果不符合條件就會不能夠訪問
predicates: #路由條件(滿足條件才會被通行)- Path=/order/**,/feign/**- After=2026-01-12T18:10:21.024727900+08:00[Asia/Shanghai]
3. Gateway Filter Factories
Predicate 是決定了請求由哪一個路由處理,Filter 過濾器是在請求處理前后做一些的邏輯的處理
Filter 又分為 Pre 和 Post 兩種類型:
Pre 類型過濾器:路由處理之前執行,也就是請求轉發給后端服務之前,例如對請求進行限流,添加額外的請求頭信息等
Post 類型過濾器:請求執行完成后,將結果返回給客戶端之前執行,例如修改響應體的主體內容,對響應路徑進行重寫等
Spring Cloud Gateway 中內置了很多的過濾器,用于攔截和處理 web 請求,根據作用范圍可以分為 GatewayFilter (作用到單個路由或者一個分組的路由上),GlobalFilter(作用到所有的路由上)
來演示一下 AddRequestParameter 用法:
接下來再訪問接口,即使 id 不傳值也通過過濾器處理之后就被賦值了
除了 AddRequestParameter,觀望中還提供了很多其它的過濾器
AddRequestHeader GatewayFilter Factory :: Spring Cloud Gateway
RequestRateLimiter 可以對通過網關的請求進行限流操作,采用的是令牌桶的算法
關于限流的算法有以下幾種:
- 固定窗口:將時間劃分為固定大小的窗口,每個窗口有一個計數器,用于記錄在該窗口內允許通過的請求數量,每通過一個請求計數器就加一,當計數器達到設定的閾值就不允許請求通過,缺點也是非常明顯,比如設定 10 分鐘可以通過 10000 個請求,前 9 分鐘都沒有請求,最后 1 分鐘處理 10000 個請求也符合要求,顯然是不合理的
- 滑動窗口:同樣將時間劃分為多個小的時間窗口,但不是按固定的大窗口重置計數器,而是將窗口滑動,這也就解決了固定窗口出現的問題,不過需要記錄多個小窗口的信息,性能開銷比較大
- 漏桶算法:就像一個漏斗型的水桶,當請求到達時,會先進入漏桶,如果漏桶未滿,則允許請求通過;如果漏桶已滿,則拒絕請求,桶以恒定的速率出水,代表處理請求的速率,無論流入的速率如何,流出的速率始終保持一致,這也就導致了不能夠處理突發的大流量
- 令牌桶算法:在漏桶的基礎上,令牌桶中會以一定的速率生成令牌,當流量小的時候,桶中就會不斷積累令牌,當遇到突發的大流量,就可以直接拿著這些令牌通過,剩余的就排隊等待令牌的生成,排隊等待這種處理方式,還可以結合其他策略,比如當排隊隊列過長時,為了避免資源過度占用,可能會對部分請求進行拒絕;或者根據業務的優先級,優先處理高優先級請求等。
再來看下一個過濾器:
Retry 就是根據當前返回的狀態碼來進行重試,可以設置狀態碼和重試次數
在上面的配置中都是使用 filter 進行配置的,如果使用 Default Filters 配置的話就是對全部路由生效
來使用 default-filters 配置一下 retry,然后把狀態碼設置為 502
之后就重新請求了 3 次
GlobalFilter 是全局過濾器,會應用到所有的路由請求上,全局過濾器通常用于實現安全性,性能監控和日志記錄等相關的全局功能
關于負載均衡,在之前的使用中就已經用到了,也就是 GlobalFilter
下面看一下 Metrics 怎么使用,首先需要引入下面的依賴
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
然后再開啟 metrics 的配置
然后再添加一下配置,用來監控詳細的信息,在 /actuator 下可以查到所有監控的鏈接和信息
過濾器的執行順序:
當一個項目中,既有 GatewayFilter 又有 GlobalFilter 時,請求路由后,網關會把當前項目中的 GatewayFilter 和 GlobalFilter 合并到一個 過濾器鏈中,并進行排序,依次執行過濾器
每一個過濾器都必須指定一個 int 類型的 order 值,默認值為 0 ,來表示過濾器的優先級,order 值越小,優先級越高,執行順序越靠前
Filter 通過實現 Order 接口或者添加@Order
注解來指定 order 值,Spring Cloud Gateway 提供的 Filter 由 Spring 指定,用戶也可以自定義 Filter,如果過濾器的 order 值一樣,會按照 defaultFilter > GatewayFilter > GlobalFilter 的順序執行
4. 自定義過濾器
4.1. 自定義 GlobalFilter
全局過濾器需要實現 GlobalFilter,Ordered 接口,然后重寫 filter 和 getOrder 方法
@Slf4j
@Component
public class CustomGlobalFilter implements GlobalFilter, Ordered {@Overridepublic Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {//Pre執行邏輯log.info("Pre Global Filter...");return chain.filter(exchange).then(Mono.fromRunnable(()->{//Post執行邏輯log.info("Post Global Filter...");}));}@Overridepublic int getOrder() {return Ordered.LOWEST_PRECEDENCE; //設置優先級}
}
關于 Mono 方法參數的說明:
之后再去發起請求,自定義的全局過濾器已經生效了
4.2. 自定義 GatewayFilter
如果需要自定義可配置的 GatewayFilter,就需要創建一個過濾器工廠,根據讀取到的配置來構造對象
定義一個類用來存儲從配置文件中讀取到的配置信息
@Data
public class CustomConfig {private String name;
}
創建一個過濾器工廠 CustomGatewayFilterFactory 繼承 AbstractGatewayFilterFactory 類,然后再實現 Ordered 接口,并添加過濾器的邏輯
@Slf4j
@Component
public class CustomGatewayFilterFactory extends AbstractGatewayFilterFactory<CustomConfig> implements Ordered {public CustomGatewayFilterFactory() {super(CustomConfig.class);}@Overridepublic GatewayFilter apply(CustomConfig config) {return new GatewayFilter() {@Overridepublic Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {//Pre類型log.info("Pre Filter,config{}",config);return chain.filter(exchange).then(Mono.fromRunnable(()->{log.info("Post Filter,config{}",config);}));}};}@Overridepublic int getOrder() {return Ordered.LOWEST_PRECEDENCE;}
}
配置文件中把自定義的過濾器名稱添加上,然后還需要添加傳入的參數,這里過濾器的名稱取的是 CustomGatewayFilterFactory 除去 GatewayFilterFactory,按照規范自定義過濾器工廠需要以 GatewayFilterFactory 為后綴
之后再啟動服務,自定義的 GatewayFilter 也生效了,并且在指定優先級相同的條件下,先執行 GatewayFilter 再執行 GlobalFilter