spring cloud gateway 官方文檔?Spring Cloud Gateway 中文文檔
什么是api網關
對于微服務的每個接口,我們都需要校驗請求的權限是否足夠,而微服務把項目細化除了許多個接口,若這些接口都要對服務進行權限校驗的話,那么無疑加重的代碼負擔和運行熟讀,而如果我使用一個統一的服務來對所有的請求進行權限校驗并將請求轉發到對應的服務,而服務的接口不對外暴露,那么就可以確保服務收到的請求是服務之間調用的,而不是用戶發起的請求調用的,而這個服務就是我們的網關,他是服務的守門神
api網關的結構如圖
他通常是后端服務的唯一入口,類似于門面模式,所有的請求都經過他手并受他掉調度,協調,過濾,如果有有多個相同的服務啟動了還可以使用負載均衡來平衡壓力
常見的開源的api網關的實現有zuul,gateway,nginx,kong
部署Gateway服務
引入依賴
<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-gateway</artifactId><version>4.0.6</version> </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>
gateway服務也需要注冊再注冊中心nacos中,這個spring-cloud-xxx(loadbalancer)和spring-cloud-starter-xxx(loadbalancer)依賴的區別是前者提供了負載均衡的核心api,但是沒有負載均衡的依賴的依賴,如果需要使用某些功能需要自己導入對應的依賴,而后者stater他提供開箱即用的體驗,他是一個空的jar包,但是會有一些元數據來告訴他需要導入哪些依賴來確保依賴運行
對項目寫一
個啟動類之后就可以路由配置了
gateway路由規則
可以寫在代碼中 不過很麻煩
寫在配置文件中要簡便許多?
uri也可以使用ws://或wss://來進行websocket請求的轉發可以直接轉發到固定的url或者通過服務發現,ws和wss的區別是前者是未加密的websocket連接后者是加密的websocket連接
http://和https:// 轉發到固定的http和https地址
Route Predicate Factories 路由斷言工廠
gateway提供了多種路由適配規則來適配多種情況
After | 需要指定一個ZonedDateTime類型的時間,他會匹配這個時間之后的請求,判斷依據是當前收到請求的時間 | predicates: # 路由條件- After=2025-09-02T13:38:51.323390200+08:00[Asia/Shanghai] |
Before | 在這個時間之前的請求 | predicates: # 路由條件- Before=2025-09-02T13:38:51.323390200+08:00[Asia/Shanghai] |
Between | 匹配倆個時間之間的請求 | predicates: # 路由條件- Between=2025-09-02T13:45:35.359524+08:00[Asia/Shanghai], 2025-09-02T13:38:51.323390200+08:00[Asia/Shanghai] |
Cookie | 包含指定的cookie,這個cookie的key是cool v是 這個hh可以是正則表達式 | predicates:- Cookie=cool, hh |
Header | 該請求包含指定的Header字段 且key為haha v滿足\d+的正則表達式 | predicates:- Header=haha, \d+ |
Host | 對請求頭中的Host進行匹配![]() | predicates:- Host=**.some.com, nlog.daxuesoutijiang.com |
Method | 匹配請求的獲取方法 Get Post | ?? predicates:- Method=Get,Post |
Path | 匹配url的路徑 | predicates:- Path=/getServer/** |
Query | 匹配url的查詢參數? 后面的參數,需要一個param,和可選的正則表達式regexp? ?hhh=ww | predicates:- Query=hhh, ww |
RemoteAddr | 匹配的是請求的遠程地址 192.168.0.1是ip地址,/16是子網掩碼,表示 匹配的是192.168.0.1 -?192.168.255.255 | predicates:- RemoteAddr=192.168.0.1/16 |
Weight | 將請求分流,如果有請求滿足了weight_high和weight_low的規則,那么就會根據weight的權重來進行分配到那個服務中進行,這里是通過相同的權重key(group1)來進*行權重的劃分的? | - id: weight_highuri: https://weighthigh.orgpredicates:- Weight=group1, 8 - id: weight_lowuri: https://weightlow.orgpredicates:- Weight=group1, 2 |
192.168.0.1這后面的.0.1拆開其實是 00000000.00000001 8為二進制,16表示從有往左的16為是網絡為那么就對應的這一段地址 192.168.0.1 - 192.168.255.255
Gateway Filter Factory 網關過濾器工廠
predicate決定了請求要經過那個路由處理,如果要對請求進行加工的話就要是使用Filter了
AddRequestParameter | 再url中添加字段,最終到url上是 127.0.0.1:8080/hh?hh=ww&www=hhh 這時多個參數的寫法 | filters:- AddRequestParameter=hh,wwAddRequestParameter=www,hhh |
AddRequestHeader | 再請求頭中添加字段h=h1 | filters:- AddRequestHeader=h,h1 |
AddResponseHeader | 再響應頭中添加字段![]() | filters:- AddResponseHeader=r1,h1 |
RemoveRequestHeader | 刪除請求頭的某個字段 | filters:- RemoveRequestHeader=del |
RemoveResponseHeader | 刪除響應頭的某個字段 | filters:- emoveResponseHeader=del |
RequestSize | 限制請求的最大大小單位字節,默認5M | filters:- name: RequestSizeargs:maxSize: 50000000 |
Retry
若后端響應失敗會重試3次,Retry還支持很多其他的參數, 比如
methord:應該初始的http方法,用org.springframework.http.HttpMethod表示
status:要重試的狀態代碼,org.springframework.http.HttpStatus這個類來表示的
series:要重試的狀態代碼系列,用?org.springframework.http.HttpStatus.Series表示
exceptions:后端拋出異常的類型要重試
還有個backoff可以去原文檔看
如果使用了Retry,他的默認配置是
-
retries
: 三次 -
series
: 5XX系列 -
methods
: GET 請求 -
exceptions
:?IOException
?和?TimeoutException
-
backoff
: disabled
filters:- name: Retryargs:retries: 3statuses: BAD_GATEWAY
RequestRateLimiter
對請求的流量進行限制,若請求被限流則會返回
HTTP 429 - Too Many Requests
使用的是令牌桶算法來限流,需要配置一個KeyResolver類型的bean,他會根據請求的類型來算出一個key,然后通過這個key來獲取到一個令牌桶,所以RequestRateLimiter是通過某種規則來對同種類型(相同key)的請求進行限流,而不是全局限流
使用的時候需要加入依賴
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis-reactive</artifactId> </dependency> 實現KeyResolver方法@Bean KeyResolver ipKeyResolver() {return exchange -> {return Mono.just(exchange.getRequest().getRemoteAddress().getAddress().getHostAddress());}; }
這個方法會將請求的ip當作key
filters:- name: RequestRateLimiterargs:redis-rate-limiter.burstCapacity: 20redis-rate-limiter.replenishRate: 12redis-rate-limiter.requestedTokens: 1key-resolver: "#{@ipKeyResolver}" # 這個就對應了實現的KeyResolver bean
redis-rate-limiter.replenishRate 這個代表的是每秒鐘允許多少個請求,每秒鐘往令牌桶中添加的令牌數
redis-rate-limiter.burstCapacity 每秒鐘能允許的最大的令牌數,令牌桶的最大容量
redis-rate-limiter.requestedTokens 每次請求消耗的令牌數
這里的name表示的是要加入哪個過濾器,args中的是參數
Defualt Filters
之前設置的filter只對當前路由生效,而如果想要使Filter對所有的路由生效就可以添加
spring.cloud.gateway.default-filters屬性
spring:cloud:gateway:? default-filters:- AddRequestParameter=p1,p1
GlobalFilter 全局過濾
GlobalFilter是gateway的全局過濾器,他和GatewayFilter的作用是相同的,他會應用到全局路由中,通常用于實現與安全性,性能檢測,日志打印相關聯的全局功能
查看GlobalFilter的監控信息
引入依賴
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-actuator</artifactId> </dependency>
添加一些配置文件
spring:cloud:gateway:# 開啟了一些指標信息的搜集 請求計數 耗時什么的metrics:enabled: true management:# 暴露所有 Actuator 端點 這樣就鞥看到更詳細的監控端口endpoints:web:exposure:include: "*"endpoint:# 更詳細的健康信息health:show-details: always# 允許遠程關閉服務 調用actuator/shutdown發起post請求shutdown:enabled: true
訪問127.0.0.1:8080/actuator?就可以看到詳細的監控信息了
當然不添加剛剛哪些配置文件也能訪問
過濾器的執行順序
當配置了global filter和gateway filter之后,網關會把他們合并到一個集合鏈中來以此執行,每一個過濾器都必須指定一個int值,值越小的優先級越高越先執行,filter通過實現order接口或者添加@order注解來決定int值
對于spring cloud filter的過濾器由spring來決定
用戶自定義的過濾器由用戶自己決定
當order的值相同時,會按照default filter -> gateway filter? -> global filter順序執行
自定義gateway filter
要自定義filter,那么就需要實現GatewayFilterFactory接口,spring提供了一個實現了GatewayFilterFactory的抽象類,我們只需要實現過濾邏輯和優先級就好
@Service public class CustomizeGatewayFilterFactory extends AbstractGatewayFilterFactory<CustomizeConfig> implements Ordered {public CustomizeGatewayFilterFactory() {super(CustomizeConfig.class);}@Overridepublic GatewayFilter apply(CustomizeConfig config) {return ((exchange, chain) -> {log.info("request url" + exchange.getRequest().getURI() + " " + config.toString());return chain.filter(exchange);}));});}@Overridepublic int getOrder() {return HIGHEST_PRECEDENCE;} }
這個過濾器會把請求的url打印出來
如果請求通過的話,就把請求傳遞到下一個斷言 return chain.filter(exchange)
如果不通過那么可以這樣直接拒絕?return exchange.getResponse().setComplete()
也可以把通過過濾器的url打印出來
return chain.filter(exchange).then(Mono.defer(() -> {//這樣獲取到的是原來的url,因為這個ServerHttpRequest是不可以修改的,要獲取到最后的url需要重新獲取到ServerHttpRequest對象log.info("end origin: {}", exchange.getRequest().getURI());URI routedUri = exchange.getAttribute(ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR);log.info("end routed: {}", routedUri);return Mono.empty();}));
自定義 全局過濾器 glocal filter
只需要實現GlobalFilter接口就號
@Service @Slf4j public class CustomizeGlobalFiler implements GlobalFilter, Ordered {@Overridepublic Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {log.info(exchange.getRequest().getURI().toString()); return chain.filter(exchange);}@Overridepublic int getOrder() {return Ordered.LOWEST_PRECEDENCE;} }
和gateway 實現很相似
end ~~