目錄
1、gateway簡介
2、gateway核心概念
3、路由
4、斷言
5、過濾器
5.1、過濾器介紹
5.2、内置局部過濾器與使用
5.3、内置全局過濾器
5.4、自定義全局過濾器
5.4.1、黑名單校驗
5.4.2、模拟登入校驗
6、一個簡單的gateway配置執行個體
1、gateway簡介
路由轉發 + 執行過濾器鍊。
網關,旨在為微服務架構提供一種簡單有效的統一的API路由管理方式。同時,基于Filter鍊的方式提供了網關的基本功能,比如:鑒權、流量控制、熔斷、路徑重寫、黑白名單、日志監控等。
基本功能如下:
- 統一入口:暴露出網關位址,作為請求唯一入口,隔離内部微服務,保障了背景服務的安全性
- 鑒權校驗:識别每個請求的權限,拒絕不符合要求的請求
- 動态路由:動态的将請求路由到不同的後端叢集中
2、gateway核心概念
- 路由(Route):由一個ID,一個目标URI(最終路由到的url位址),一組斷言(比對條件判斷)和一組過濾器定義。如果斷言為真,則路由比對。
- 斷言(Predicate):通過斷言比對http請求中的任何内容(請求頭、請求參數等),如果比對成功,則比對斷言所在路由。
- 過濾器(Filter):在請求前後執行業務邏輯,比如鑒權、日志監控、流量控制、修改請求頭、修改響應等。
3、路由
spring:
cloud:
gateway:
routes:
- id: manager # 路由唯一辨別
uri: lb://manager_server # 路由指向目的地URL或服務名,用戶端請求最終被轉發到的微服務
predicates:
- Path=/manager/** # 斷言:以manager開頭的請求都負載到manager_server服務
filters:
- RewritePath=/manager/(?<segment>.*), /$\{segment} # 過濾器:過濾掉url裡的manager,例如http://ip:port/manager/test -> http://ip:port/test
order: 5 # 用于多個Route之間的排序,數值越小越靠前,比對優先級越高
4、斷言
spring:
cloud:
gateway:
routes:
- id: manager
uri: https://manager_server
predicates:
- After=2017-01-20T17:42:47.789-07:00[America/Denver] # 時間點後比對
- Before=2017-01-20T17:42:47.789-07:00[America/Denver] # 時間點前比對
- Between=2017-01-20T17:42:47.789-07:00[America/Denver],2017-01-21T17:42:47.789-07:00[America/Denver] # 時間區間比對
- Cookie=chocolate, ch.p # 指定cookie正則比對
- Header=X-Request-Id, \d+ # 指定Header正則比對
- Host=**.somehost.org,**.anotherhost.org # 請求Host比對
- Method=GET,POST # 請求Method比對指定請求方式
- Path=/red/{segment},/blue/{segment} # 請求路徑正則比對
- Query=green # 請求包含某參數
- Query=red, gree. # 請求包含某參數并且參數值比對正規表達式(比對red;green,greet,gree...)
- RemoteAddr=192.168.1.1/24 # 遠端位址比對
# 設定分組和權重,按照路由權重選擇同一個分組中的路由
- id: preManager1
uri: https://preManager1
predicates:
- Weight=group1, 2
- id: preManager2
uri: https://preManager2
predicates:
- Weight=group1, 8
5、過濾器
5.1、過濾器介紹
按生命周期分類
- 前置(pre)過濾器: 在請求被路由之前調用:在chain.filter(exchange)前編寫過濾器邏輯
- 後置(post)過濾器: 在路由到微服務之後調用:通過chain.filter(exchange).then(Mono.fromRunnable(() -> {過濾器邏輯})實作
按類型分類
- 局部(GatewayFilter)過濾器:作用在某一個路由上,使用時需要關聯指定的路由
- 全局(GlobalFilter)過濾器:作用在所有路由上,不需要在配置檔案中配置
5.2、内置局部過濾器與使用
spring:
cloud:
gateway:
routes:
- id: gateway_filter
uri: https://example.org
predicates:
- Path=/red/{segment}
filters:
# 1、為原始請求添加Header。headerName:X-Request-red,headerValue:blue。
- AddRequestHeader=X-Request-red, blue
- AddRequestHeadersIfNotPresent=X-Request-Color-1:blue,X-Request-Color-2:green
# 2、為原始請求添加參數。參數名,參數值
- AddRequestParameter=red, blue
# 3、為原始響應添加Header
- AddResponseHeader=X-Response-Red, Blue
# 4、剔除響應頭中重複的值
- DedupeResponseHeader=Access-Control-Allow-Credentials Access-Control-Allow-Origin
# 5、為原始請求路徑添加字首
- PrefixPath=/mypath
# 6、配置該過濾器後,會原始請求的host頭資訊,并原封不動的轉發出去,而不是被gateway的http用戶端重置。
- PreserveHostHeader
# 7、将原始請求重定向到指定的URL,參數為http狀态碼及重定向的url
- RedirectTo=302, https://acme.org
# 8、移除響應Body中的指定key
- RemoveJsonAttributesResponseBody=id,color
# 9、移除原始請求中的指定Header
- RemoveRequestHeader=X-Request-Foo
# 10、移除原始請求中的指定參數
- RemoveRequestParameter=red
# 11、移除響應中的指定Header
- RemoveResponseHeader=X-Response-Foo
spring:
cloud:
gateway:
routes:
- id: gateway_filter
uri: https://example.org
predicates:
- Path=/red/{segment}
filters:
# 12、請求限流,限流算法為令牌桶,以下示例為根據使用者id做限流
# @Configuration
# public class RateLimiterConfig {
# @Bean
# public KeyResolver userKeyResolver() {
# return exchange -> Mono.just(Objects.requireNonNull(exchange.getRequest().getQueryParams().getFirst("userId")));
# }
# }
- name: RequestRateLimiter
args:
redis-rate-limiter.replenishRate: 10 # 允許使用者每秒處理的請求數
redis-rate-limiter.burstCapacity: 20 # 令牌桶的容量,即允許在 1 秒内完成的最大請求數。設定為 0 則表示拒絕所有請求。
key-resolver: "#{@userKeyResolver}" # 一個引用名為 userKeyResolver 的 bean 的 SpEL 表達式
# 13、重寫原始的請求路徑
- RewritePath=/red/?(?<segment>.*), /$\{segment}
# 14、重寫響應中的某個Header
- RewriteResponseHeader=X-Response-Red, , password=[^&]+, password=***
# 15、在轉發請求之前,強制執行websession::save操作,儲存會話狀态
- SaveSession
# 16、修改原始的請求路徑
- SetPath=/{segment}
# 17、修改原始請求中的指定Header值
- SetRequestHeader=X-Request-Red, Blue
# 18、修改原始響應中的指定Header值
- SetResponseHeader=X-Response-Red, Blue
# 19、修改原始響應的響應碼
- SetStatus=401
# 20、剝離原始請求路徑
- StripPrefix=2
# 21、請求重試
- name: Retry
args:
retries: 3 # 重試次數
statuses: BAD_GATEWAY # 應被重試的 HTTP Status Codes
methods: GET,POST # 應被重試的 HTTP Methods
backoff: # 為重試配置指數級的 backoff。重試時間間隔的計算公式為 firstBackoff * (factor ^ n),n 是重試的次數;如果設定了 maxBackoff,最大的 backoff 限制為 maxBackoff. 如果 basedOnPreviousValue 設定為 true, backoff 計算公式為 prevBackoff * factor.
firstBackoff: 10ms
maxBackoff: 50ms
factor: 2
basedOnPreviousValue: false
# 22、設定允許接收最大請求包的大小。如果請求包大小超過設定的值,則返413Payload Too Large
- name: RequestSize
args:
maxSize: 5000000
5.3、内置全局過濾器
- GatewayMetricsFilter(0):統計一些網關的性能名額
- RouteToRequestUrlFilter(10000):把浏覽器的URL請求的Path路徑添加到路由的URI之中。
- NettyRoutingFilter(2147483647):通過HttpClient用戶端轉發真實的URL,并存儲傳回的結果。
- NettyWriteResponseFilter(-1):在所有的其它的過濾器執行完成之後運作,将響應的資料發送給網關的用戶端。
- ForwardRoutingFilter(2147483647):轉發路由過濾器,若URI是forward模式,過濾器會将請求轉發到DispatcherHandler來處理請求。
- ForwardPathFilter(0):解析路徑,并将路徑轉發。
- LoadBalancerClientFilter(10100):負載均衡,解析服務名,擷取真實服務位址。
- RemoveCachedBodyFilter(-2147483648):清除網關上下文中的緩存的請求Body。
- WebsocketRoutingFilter(2147483646):如果請求中的ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR 屬性對應的URL字首為 ws 或 wss,會使用Spring Web Socket 子產品轉發WebSocket請求。WebSockets可以使用路由進行負載均衡。
- AdaptCachedBodyGlobalFilter(-2147482648):從請求中擷取body緩存到網關上下文。
5.4、自定義全局過濾器
建立自定義全局過濾器類 ,實作GlobalFilter和Ordered兩個接口。
- GlobalFilter:全局過濾攔截器
- Ordered:攔截器的順序,數字越低,優先級越高
5.4.1、黑名單校驗
/**
* 定義全局過濾器,會對所有路由生效
*/
@Slf4j
@Component // 讓容器掃描到,等同于注冊了
public class BlackListFilter implements GlobalFilter, Ordered {
// 模拟黑名單(實際可以去資料庫或者redis中查詢)
private static List<String> blackList = new ArrayList<>();
static {
blackList.add("0:0:0:0:0:0:0:1"); // 模拟本機位址
}
/**
* 過濾器核心方法
* @param exchange 封裝了request和response對象的上下文
* @param chain 網關過濾器鍊(包含全局過濾器和單路由過濾器)
* @return
*/
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
// 思路:擷取用戶端ip,判斷是否在黑名單中,在的話就拒絕通路,不在的話就放行
ServerHttpRequest request = exchange.getRequest();
ServerHttpResponse response = exchange.getResponse();
// 從request對象中擷取用戶端ip
String clientIp = request.getRemoteAddress().getHostString();
// 拿着clientIp去黑名單中查詢,存在的話就決絕通路
if(blackList.contains(clientIp)) {
// 拒絕通路,傳回
response.setStatusCode(HttpStatus.UNAUTHORIZED); // 狀态碼
log.debug("=====>IP:" + clientIp + " 在⿊名單中,将被拒絕通路!");
String data = "Request be denied!";
DataBuffer wrap = response.bufferFactory().wrap(data.getBytes());
return response.writeWith(Mono.just(wrap));
}
// 合法請求,放行,執行後續的過濾器
return chain.filter(exchange);
}
/**
* @return 過濾器的順序(優先級),數值越小,優先級越高
*/
@Override
public int getOrder() {
return 0;
}
}
5.4.2、模拟登入校驗
在過濾器中檢查請求中是否攜帶token請求頭。如果token請求頭存在則放行;如果token為空或者不存在則傳回認證失敗狀态碼。
@Component
public class MyGlobalFilter implements GlobalFilter,Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
boolean token = exchange.getRequest().getHeaders().containsKey("token");
System.out.println("----全局過濾器token----"+token);
if (!token){
exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
ServerHttpResponse response = exchange.getResponse();
return response.setComplete();
}
return chain.filter(exchange);
}
@Override
public int getOrder() {
return 1;
}
}
6、一個簡單的gateway配置執行個體
spring:
cloud:
gateway:
discovery:
locator:
# 表明Gateway開啟服務注冊和發現的功能,并且Spring Cloud Gateway自動根據服務發現為每一個服務建立了一個router,這個router将以服務名開頭的請求路徑轉發到對應的服務
enabled: true
# 将請求路徑上的服務名配置為小寫(因為服務注冊的時候,向注冊中心注冊時将服務名轉成大寫的了)
lower-case-service-id: true
routes:
# 系統管理
- id: sys-mgt
uri: lb://sysmgt
predicates:
- Path=/sys-mgt/** #以sys-mgt開頭的請求都負載到sysmgt服務
- Method=GET #隻比對GET請求
filters:
- RewritePath=/sys-mgt/(?<segment>.*), /$\{segment} #過濾掉url裡的sys-mgt,例如http://ip:port/sys-mgt/test -> http://ip:port/test
- PrefixPath=/mgt #為請求添加/mgt字首,再結合RewritePath過濾器,http://ip:port/sys-mgt/test -> http://ip:port/mgt/test
以上内容為個人學習了解,如有問題,歡迎在評論區指出。
部分内容截取自網絡,如有侵權,聯系作者删除。