3、SpringCloud Gateway

Spring Cloud Gateway 是Spring Cloud團隊的一個全新專案,基于Spring 5.0、SpringBoot2.0、Project Reactor 等技術開發的網關,旨在為微服務架構提供一種簡單有效統一的API路由管理方式,
Spring Cloud Gateway 作為SpringCloud生態系統中的網關,目標是替代Netflix Zuul,Gateway不僅提供統一路由方式,并且基于Filter鏈的方式提供網關的基本功能,例如:安全,監控/指標,和限流,
總結:微服務網關就是一個系統,通過暴露該微服務網關系統,方便我們進行相關的鑒權,安全控制,日志統一處理,易于監控,限流等相關功能,
實作微服務網關的技術有很多,
- nginx:Nginx (engine x) 是一個高性能的HTTP和反向代理web服務器,同時也提供了IMAP/POP3/SMTP服務
- zuul :Zuul 是 Netflflix 出品的一個基于 JVM 路由和服務端的負載均衡器,
- spring-cloud-gateway:是spring 出品的基于spring的網關專案,集成斷路器,路徑重寫,性能比Zuul好,
我們使用gateway這個網關技術,無縫銜接到基于spring cloud的微服務開發中來,
gateway官網:
https://spring.io/projects/spring-cloud-gateway
3.1 Gateway作業原理
我們在學習Gateway之前,先弄清楚Gateway的作業原理,后面使用它的各個功能時,就知道該如何使用了,作業流程圖如下:

Gateway的執行流程如下:
1:Gateway的客戶端回向Spring Cloud Gateway發起請求,請求首先會被HttpWebHandlerAdapter進行提取組裝成網關的背景關系,然后網關的背景關系會傳遞到DispatcherHandler,
2:DispatcherHandler是所有請求的分發處理器,DispatcherHandler主要負責分發請求對應的處理器,比如將請求分發到對應RoutePredicateHandlerMapping(路由斷言處理器映射器),
3:路由斷言處理映射器主要用于路由的查找,以及找到路由后回傳對應的FilteringWebHandler,
4:FilteringWebHandler主要負責組裝Filter鏈表并呼叫Filter執行一系列Filter處理,然后把請求轉到后端對應的代理服務處理,處理完畢后,將Response回傳到Gateway客戶端,
在Filter鏈中,通過虛線分割Filter的原因是,過濾器可以在轉發請求之前處理或者接收到被代理服務的回傳結果之后處理,所有的Pre型別的Filter執行完畢之后,才會轉發請求到被代理的服務處理,被代理的服務把所有請求完畢之后,才會執行Post型別的過濾器,
3.2 Gateway路由
Gateway路由配置分為基于配置的靜態路由設定和基于代碼動態路由配置,
靜態路由是指在application.yml中把路由資訊配置好了,而動態路由則支持在代碼中動態加載路由資訊,更加靈活,我們接下來把這2種路由操作都實作一次,
3.2.1 業務說明

如上圖:
1:用戶所有請求以/order開始的請求,都路由到 hailtaxi-order服務
2:用戶所有請求以/driver開始的請求,都路由到 hailtaxi-driver服務
3:用戶所有請求以/pay開始的請求,都路由到 hailtaxi-pay服務
3.2.2 基于配置路由設定

如上圖所示,正是Gateway靜態路由配置:
1:用戶所有請求以/order開始的請求,都路由到 hailtaxi-order服務
2:用戶所有請求以/driver開始的請求,都路由到 hailtaxi-driver服務
3:用戶所有請求以/pay開始的請求,都路由到 hailtaxi-pay服務
配置引數說明:
routes:路由配置
- id:唯一識別符號
uri:路由地址,可以是 lb://IP:埠 也可以是 lb://${spring.application.name}
predicates:斷言,是指路由條件
- Path=/driver/**:路由條件,Predicate 接受一個輸入引數,回傳一個布林值結果,這里表示匹配所有以driver開始的請求,
filters:過濾器
- StripPrefix=1:真實路由的時候,去掉第1個路徑,路徑個數以/分割區分
測驗url:http://localhost:8001/driver/info/1
3.2.3 基于代碼路由配置
我們同樣實作上面的功能,但這里基于代碼方式實作,所有路由規則我們可以從資料庫中讀取并加載到程式中,基于代碼的路由配置我們只需要創建RouteLocator并添加路由配置即可,代碼如下:
/***
* 路由配置
* @param builder
* @return
*/
@Bean
public RouteLocator routeLocator(RouteLocatorBuilder builder) {
return builder.routes()
.route("hailtaxi-driver", r -> r.path("/driver/**").uri("lb://hailtaxi-driver"))
.route("hailtaxi-order", r -> r.path("/order/**").uri("lb://hailtaxi-order"))
.route("hailtaxi-pay", r -> r.path("/pay/**").uri("lb://hailtaxi-pay"))
.build();
}
在真實場景中,基于組態檔的方式更直觀、簡介,但代碼的路由配置是更強大,可以實作很豐富的功能,可以把路由規則存在資料庫中,每次直接從資料庫中加載規則,這樣的好處是可以動態重繪路由規則,通常應用于權限系統動態配置,
spring:
cloud:
gateway:
#路由配置
routes:
#唯一識別符號
- id: hailtaxi-driver
uri: lb://hailtaxi-driver
#路由斷言
predicates:
- Path=/driver/**
#唯一識別符號
- id: hailtaxi-order
uri: lb://hailtaxi-order
#路由斷言
predicates:
- Path=/order/**
#唯一識別符號
- id: hailtaxi-pay
uri: lb://hailtaxi-pay
#路由斷言
predicates:
- Path=/pay/**
3.2.4 Gateway-Predicate
上面路由匹配規則中我們都用了- Path方式,其實就是路徑匹配方式,除了路徑匹配方式,Gateway還支持很多豐富的匹配方式,我們對這些方式分別進行講解,
關于Predicate學習地址,可以參考官網:
https://docs.spring.io/spring-cloud-gateway/docs/2.2.5.RELEASE/reference/html/#gateway-request-predicates-factories
或者:
https://cloud.spring.io/spring-cloud-static/spring-cloud-gateway/2.1.1.RELEASE/single/spring-cloud-gateway.html#gateway-request-predicates-factories
routes下面的屬性含義如下:
id:我們自定義的路由 ID,保持唯一
uri:目標服務地址
predicates:路由條件,Predicate 接受一個輸入引數,回傳一個布林值結果,該屬性包含多種默認方法來將 Predicate 組合成其他復雜的邏輯(比如:與,或,非)
Predicate 來源于 Java 8,Predicate 接受一個輸入引數,回傳一個布林值結果,該介面包含多種默認方法來將 Predicate 組合成其他復雜的邏輯(比如:與,或,非),
在 Spring Cloud Gateway 中 Spring 利用 Predicate 的特性實作了各種路由匹配規則,通過 Header、請求引數等不同的條件來作為條件匹配到對應的路由,
下面的一張圖(來自網路)總結了 Spring Cloud 內置的幾種 Predicate 的實作:

我們在這里講解幾個斷言匹配 方式,
Cookie:
Gateway的Cookie匹配接收兩個引數:一個是 Cookie name ,一個是正則運算式,路由規則就是通過獲取對應的 Cookie name 值和正則運算式去匹配,如果匹配上就會執行路由,如果沒有匹配上則不執行,如下配置:
gateway:
routes:
- id: hailtaxi-driver
uri: lb://hailtaxi-driver
predicates:
- Path=/driver/**
- Cookie=username,itheima
這里表示請求攜帶了cookie為username的資料,并且值為itheima,就允許通過,
Header 匹配:
Header 匹配 和 Cookie 匹配 一樣,也是接收兩個引數,一個 header 中屬性名稱和一個正則運算式,這個屬性值和正則運算式匹配則執行,配置如下:
gateway:
routes:
- id: hailtaxi-driver
uri: lb://hailtaxi-driver
predicates:
- Path=/driver/**
- Header=token,^(?!\d+$)[\da-zA-Z]+$
上面的匹配規則,就是請求頭要有token屬性,并且值必須為數字和字母組合的正則運算式,例如攜帶token=19and30就可以通過訪問,
請求方式匹配:
通過請求的方式是 POST、GET、PUT、DELETE 等進行路由,配置如下:
gateway:
routes:
- id: hailtaxi-driver
uri: lb://hailtaxi-driver
predicates:
- Path=/driver/**
- Method=GET,POST
通過yml的完整代碼如下(注釋掉java的配置)
server:
port: 8001
spring:
application:
name: hailtaxi-gateway
main:
allow-bean-definition-overriding: true
cloud:
#Consul配置
consul:
host: 127.0.0.1
port: 8500
discovery:
#注冊到Consul中的服務名字
service-name: ${spring.application.name}
#注冊的服務的實體 Id,最好不要重復,這里參考官網建議的方式 帶亂數 默認:應用名:port
#instance-id: ${spring.application.name}:${vcap.application.instance_id:${spring.application.i nstance_id:${random.value}}}
# 自定義實體id為:應用名:ip:port
instance-id: ${spring.application.name}:${spring.cloud.client.ip-address}:${server.port}
prefer-ip-address: true
# 開啟服務注冊
register: true
# 開啟服務發現
enabled: true
#2 分鐘之后健康檢查未通過取消注冊
health-check-critical-timeout: 2m
#consul 健康檢查的輪詢周期
health-check-interval: 10s
gateway:
#路由配置
routes:
#唯一識別符號
- id: hailtaxi-driver
uri: lb://hailtaxi-driver
#路由斷言
predicates:
- Path=/driver/**
- Cookie=username,itheima
- Header=token,^(?!\d+$)[\da-zA-Z]+$
- Method=GET,POST
#唯一識別符號
- id: hailtaxi-order
uri: lb://hailtaxi-order
#路由斷言
predicates:
- Path=/order/**
#唯一識別符號
- id: hailtaxi-pay
uri: lb://hailtaxi-pay
#路由斷言
predicates:
- Path=/pay/**
3.2.5、斷言原始碼剖析
拿Cookie斷言來說,首先看它的體系結構

public class CookieRoutePredicateFactory
extends AbstractRoutePredicateFactory<CookieRoutePredicateFactory.Config> {
/**
* Name key.
*/
public static final String NAME_KEY = "name";
/**
* Regexp key.
*/
public static final String REGEXP_KEY = "regexp";
public CookieRoutePredicateFactory() {
super(Config.class);
}
/*
通過shortcutFieldOrder方法設定Config配置類中的屬性,需要根據具體的規則來設定
通過shortcutType方法獲取具體規則,具體參看:org.springframework.cloud.gateway.support.ShortcutConfigurable.ShortcutType
規則包括以下幾種:
DEFAULT : 按照shortcutFieldOrder順序依次賦值
*/
@Override
public List<String> shortcutFieldOrder() {
return Arrays.asList(NAME_KEY, REGEXP_KEY);
}
@Override
public Predicate<ServerWebExchange> apply(Config config) {
return new GatewayPredicate() {
@Override
public boolean test(ServerWebExchange exchange) {
List<HttpCookie> cookies = exchange.getRequest().getCookies()
.get(config.name);
if (cookies == null) {
return false;
}
for (HttpCookie cookie : cookies) {
if (cookie.getValue().matches(config.regexp)) {
return true;
}
}
return false;
}
@Override
public String toString() {
return String.format("Cookie: name=%s regexp=%s", config.name,
config.regexp);
}
};
}
/*
內部配置類是用來接收在組態檔中配置的引數的
routes:
#唯一識別符號
- id: hailtaxi-driver
uri: lb://hailtaxi-driver
#路由斷言
predicates:
- Cookie=username,itheima
*/
@Validated
public static class Config {
@NotEmpty
private String name;
@NotEmpty
private String regexp;
public String getName() {
return name;
}
public Config setName(String name) {
this.name = name;
return this;
}
public String getRegexp() {
return regexp;
}
public Config setRegexp(String regexp) {
this.regexp = regexp;
return this;
}
}
}
盡管Spring Cloud Gateway已經包含了很多路由匹配規則,有時候我們需要開發自定義路由匹配規則來滿足需求,下面簡單的介紹一下如何自定義路由匹配規則,
案例
需求:轉發帶token的請求到hailtaxi-drvier服務中,這里定義請求帶token是指包含某個請求頭的請求,至于是什么請求頭可以由配置指定
1、修改組態檔
gateway:
#路由配置
routes:
#唯一識別符號
- id: hailtaxi-driver
uri: lb://hailtaxi-driver
#路由斷言
predicates:
# 自定義一個Token斷言,如果請求包含Authorization的token資訊則通過
- Token=Authorization
2、創建 RoutePredicateFactory
斷言工廠默認命名規則必須按照"名稱"+RoutePredicateFactory,如上TokenRoutePredicateFactory的斷言名稱為Token
@Slf4j
@Component // 要交給spring容器管理
public class TokenRoutePredicateFactory extends AbstractRoutePredicateFactory<TokenRoutePredicateFactory.Config> {
public TokenRoutePredicateFactory() {
super(Config.class);
}
public Predicate<ServerWebExchange> apply(Config config) {
return exchange -> {
// 列印組態檔引數值
String headerName = config.getHeaderName();
HttpHeaders headers = exchange.getRequest().getHeaders();
List<String> header = headers.get(headerName);
log.info("Token Predicate headers:{}", header);
// 斷言回傳的是boolean值
return header!=null && header.size()>0;
};
}
@Override
public List<String> shortcutFieldOrder() {
return Arrays.asList("headerName");//指定組態檔中加載到的配置資訊應填充到Config的哪個屬性上
}
@Override
public ShortcutType shortcutType() {
return ShortcutType.DEFAULT;
}
@Data
public static class Config { //static class
private String headerName;//存盤從組態檔中加載的配置
}
}
啟動測驗:http://localhost:8001/driver/info/1
3.3 Gateway過濾器
Spring Cloud Gateway根據作用范圍劃分為GatewayFilter和GlobalFilter,二者區別如下:
-
GatewayFilter : 需要通過spring.cloud.routes.filters 配置在具體路由下,只作用在當前路由上或通過spring.cloud.default-filters配置在全域,作用在所有路由上;gateway內置了多種過濾器工廠,配套的過濾器可以直接使用,如下圖所示:


-
GlobalFilter : 全域過濾器,不需要在組態檔中配置,作用在所有的路由上,最終通過GatewayFilterAdapter包裝成GatewayFilterChain可識別的過濾器,它為請求業務以及路由的URI轉換為真實業務服務的請求地址的核心過濾器,不需要配置,系統初始化時加載,并作用在每個路由上,

過濾器作為Gateway的重要功能,常用于請求鑒權、服務呼叫時長統計、修改請求或回應header、限流、去除路徑等等,
關于Gateway過濾器的更多使用,大家可以參考官方地址:
https://docs.spring.io/spring-cloud-gateway/docs/2.2.5.RELEASE/reference/html/#gatewayfilter-factories
或者:
https://cloud.spring.io/spring-cloud-static/spring-cloud-gateway/2.1.1.RELEASE/single/spring-cloud-gateway.html#_gatewayfilter_factories
3.3.1 過濾器分類
默認過濾器:出廠自帶,實作好了拿來就用,不需要實作
全域默認過濾器
區域默認過濾器
自定義過濾器:根據需求自己實作,實作后需配置,然后才能用哦,
全域過濾器:作用在所有路由上,
區域過濾器:配置在具體路由下,只作用在當前路由上,
默認過濾器十好幾個,常見如下:
| 過濾器名稱 | 說明 | 對應的類 | 父類 |
|---|---|---|---|
| AddRequestHeader | 對匹配上的請求加上Header | AddRequestHeaderGatewayFilterFactory | AbstractNameValueGatewayFilterFactory |
| AddRequestParameters | 對匹配上的請求路由 | AddRequestHeaderGatewayFilterFactory | AbstractNameValueGatewayFilterFactory |
| AddResponseHeader | 對從網關回傳的回應添加Header | AddResponseHeaderGatewayFilterFactory | AbstractNameValueGatewayFilterFactory |
| StripPrefix | 對匹配上的請求路徑去除前綴 | StripPrefixGatewayFilterFactory | AbstractGatewayFilterFactory |
3.3.2 默認過濾器的使用
所謂默認過濾器就是系統自帶的,有很多,這里簡要說明幾個:(通過java配置,注釋掉yaml配置)
1)添加回應頭
AddResponseHeaderGatewayFilterFactory 屬于 GatewayFilter
對輸出回應頭設定屬性,比如對輸出的回應設定其頭部屬性名稱為:X-Response-Default-MyName , 值為itheima
修改組態檔,配置如下:
spring:
cloud:
gateway:
# 配置全域默認過濾器 作用在所有路由上,也可單獨為某個路由配置
default-filters:
# 往回應過濾器中加入資訊
- AddResponseHeader=X-Response-Default-MyName,itheima
請求http://localhost:8001/driver/info/1,回應資料添加了X-Response-Default-MyName: itheima,如下圖:

2)前綴處理
在專案中做開發對接介面的時候,我們很多時候需要統一API路徑,比如統一以/api開始的請求呼叫hailtaxi-driver服務,但真實服務介面地址又沒有/api路徑,我們可以使用Gateway的過濾器處理請求路徑,
在gateway中可以通過配置路由的過濾器StripPrefix實作映射路徑中的前綴處理,我們來使用一下該過濾器,再進一步做說明,
gateway:
routes:
- id: hailtaxi-driver
uri: lb://hailtaxi-driver
predicates:
- Path=/api/driver/**
filters:
- StripPrefix=1
此處- StripPrefix=1表示真實請求地址是當前用戶請求以/api開始的uri中去除第1個路徑/api.
上面配置最終執行如下表:
| 配置 | 路由地址 | 訪問地址 |
|---|---|---|
| StripPrefix=1 | http://localhost:8001/api/driver/info/2 | http://localhost:18081/driver/info/2 |
| StripPrefix=2 | http://localhost:8001/api/suri/driver/info/2 | http://localhost:18081/driver/info/2 |
有時候為了簡化用戶請求地址,比如用戶請求http://localhost:8001/info/1我們想統一路由到http://localhost:18081/driver/info/1,可以使用PrefixPath過濾器增加前綴,
gateway:
routes:
- id: hailtaxi-driver
uri: lb://hailtaxi-driver
predicates:
- Path=/**
filters:
- PrefixPath=/driver
上面配置最終執行如下表:
| 配置 | 路由地址 | 訪問地址 |
|---|---|---|
| - PrefixPath=/driver | http://localhost:8001/info/2 | http://localhost:18081/driver/info/2 |
3.3.3自定義GatewayFilter
1、實作GatewayFilter介面
GatewayFilter 一般作用在某一個路由上,需要實體化創建才能使用,區域過濾器需要實作介面GatewayFilter、Ordered,
創建com.itheima.filter.PayFilter代碼如下:
public class PayFilter implements GatewayFilter,Ordered {
/***
* 過濾器執行攔截
* @param exchange
* @param chain
* @return
*/
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
System.out.println("GatewayFilter攔截器執行---pre-----PayFilter");
return chain.filter(exchange).then(Mono.fromRunnable(()->{
System.out.println("GatewayFilter攔截器執行---post-----PayFilter");
}));
}
@Override
public int getOrder() {
return 0;
}
}
使用區域過濾器:(使用下面RouteLocator的時候,組態檔中的路由記得注釋或洗掉)
/***
* 路由配置
* @param builder
* @return
*/
@Bean
public RouteLocator routeLocator(RouteLocatorBuilder builder) {
return builder.routes()
.route("hailtaxi-driver", r -> r.path("/api/driver/**")
.and().cookie("username","itheima")
.and().header("token","123456")
.filters(f->f.filter(new PayFilter()).addResponseHeader("X-Response-Default-MyName", "itheima")
.addRequestHeader("myheader", "1234567")
.stripPrefix(1)
)
// .and().method(HttpMethod.POST)
.uri("lb://hailtaxi-driver")
//.filter(new PayFilter())
)
.route("hailtaxi-order", r -> r.path("/order/**").uri("lb://hailtaxi-order"))
.route("hailtaxi-pay", r -> r.path("/pay/**").uri("lb://hailtaxi-pay"))
.build();
}
為了更好看到效果,我們在RouterFilter添加System.out.println("GlobalFilter攔截器執行");再訪問測驗,
訪問:http://localhost:8001/api/driver/info/1,注意使用postman發送請求時添加請求頭,添加cookie,
2、繼承GatewayFilterFactory
如果定義區域過濾器,想在組態檔中進行配置來使用,可以繼承AbstractGatewayFilterFactory<T>抽象類或者AbstractNameValueGatewayFilterFactory
整個體系結構為:

這兩個抽象類的區別就是前者接收一個引數(像StripPrefix和我們創建的這種),后者接收兩個引數(像AddResponseHeader)
代碼的撰寫可以參考:StripPrefixGatewayFilterFactory 和 AddRequestHeaderGatewayFilterFactory
過濾器工廠默認命名規則必須按照"名稱"+GatewayFilterFactory`,如上StripPrefixGatewayFilterFactory的過濾器名稱為StripPrefix
2.1、繼承AbstractGatewayFilterFactory
需求:
在網關中統一支付方式,撰寫一個過濾器:PayMethodGatewayFilterFactory,
1、撰寫過濾器
@Slf4j
@Component //一定要將其交給spring容器管理
public class PayMethodGatewayFilterFactory extends AbstractGatewayFilterFactory<PayMethodGatewayFilterFactory.Config> {
public PayMethodGatewayFilterFactory() {
super(Config.class);
}
@Override
public GatewayFilter apply(Config config) {
return (exchange, chain) -> {
String paymethod = config.getPayMethod();
String msg = config.getMsg();
log.info("PayMethodGatewayFilterFactory 加載到的配置資訊為:{}---{}",paymethod,msg);
//將paymethod添加到請求頭中
exchange.getRequest().mutate().header("paymethod",paymethod);
return chain.filter(exchange);
};
}
@Override
public List<String> shortcutFieldOrder() {
return Arrays.asList("payMethod","msg");//指定從yml中提前出來的配置資訊填充到配置類中哪個屬性,按規則配置
}
@Override
public ShortcutType shortcutType() {
return ShortcutType.DEFAULT;//默認規則
}
/**
* 加載從yml中提取出來的配置資訊
*/
@Data
public static class Config {
private String payMethod;
private String msg;
}
}
2、組態檔中使用如下:
gateway:
#路由配置
routes:
#唯一識別符號
- id: hailtaxi-driver
uri: lb://hailtaxi-driver
#路由斷言
predicates:
- Path=/driver/**
- Cookie=username,itheima
- Header=token,^(?!\d+$)[\da-zA-Z]+$
- Method=GET,POST
- Token=Authorization
filters:
- PayMethod=alipay,業務整合
再次測驗,查看hailtaxi-driver 服務接收到請求后是否多了paymethod請求頭資訊
2.2、繼承AbstractNameValueGatewayFilterFactory
直接查看AddRequestHeaderGatewayFilterFactory 原始碼,分析即可!
3.3.4 自定義GlobalFilter
定義全域過濾器需要實作GlobalFilter,Ordered介面:
GlobalFilter:過濾器攔截處理方法
Ordered:過濾器也有多個,這里主要定義過濾器執行順序,里面有個方法getOrder()會回傳過濾器執行順序,回傳值越小,越靠前執行
需求:
我們創建全域過濾器并完成常見業務用戶權限校驗,如果請求中有帶有一個名字為token的請求引數,則認為請求有效放行,如果沒有則攔截提示授權無效,
創建全域過濾器:com.itheima.filter.RouterFilter,代碼如下:
@Component
public class RouterFilter implements GlobalFilter,Ordered {
/***
* 路由攔截
* @param exchange
* @param chain
* @return
*/
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
log.info("RouterFilter----------------");
//獲取請求引數
String token = exchange.getRequest().getQueryParams().getFirst("token");
//如果token為空,則表示沒有登錄
if(StringUtils.isEmpty(token)){
//沒登錄,狀態設定403
exchange.getResponse().setStatusCode(HttpStatus.PAYLOAD_TOO_LARGE);
//結束請求
return exchange.getResponse().setComplete();
}
//放行
return chain.filter(exchange);
}
/***
* 攔截器順序
* @return
*/
@Override
public int getOrder() {
return 0;
}
}
此時請求,我們不攜帶token引數,效果如下:

我們攜帶token引數則可以正常訪問,效果如下:

3.4 跨域配置
出于瀏覽器的同源策略限制,同源策略(Sameoriginpolicy)是一種約定,它是瀏覽器最核心也最基本的安全功能,如果缺少了同源策略,則瀏覽器的正常功能可能都會受到影響,可以說Web是構建在同源策略基礎之上的,瀏覽器只是針對同源策略的一種實作,同源策略會阻止一個域的javascript腳本和另外一個域的內容進行互動,所謂同源(即指在同一個域)就是兩個頁面具有相同的協議(protocol),主機(host)和埠號(port),
在Spring Cloud Gateway中配置跨域是非常簡單的,如下面application.yml所示:
gateway:
globalcors:
corsConfigurations:
'[/**]':
allowedOrigins: "*"
allowedMethods:
- GET
- POST
- PUT
但如果涉及到Cookie跨域,上面的配置就不生效了,如果涉及到Cookie跨域,需要創建CorsWebFilter過濾器,代碼如下:
/**
* 配置跨域
* @return
*/
@Bean
public CorsWebFilter corsFilter() {
CorsConfiguration config = new CorsConfiguration();
// cookie跨域
config.setAllowCredentials(Boolean.TRUE);
config.addAllowedMethod("*");
config.addAllowedOrigin("*");
config.addAllowedHeader("*");
// 配置前端js允許訪問的自定義回應頭
config.addExposedHeader("Authorization");
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(new PathPatternParser());
source.registerCorsConfiguration("/**", config);
return new CorsWebFilter(source);
}
3.5 限流
網關可以做很多的事情,比如,限流,當我們的系統 被頻繁的請求的時候,就有可能 將系統壓垮,所以 為了解決這個問題,需要在每一個微服務中做限流操作,但是如果有了網關,那么就可以在網關系統做限流,因為所有的請求都需要先通過網關系統才能路由到微服務中,
3.5.1 漏桶演算法講解

漏桶演算法是常見的限流演算法之一,我們講解一下漏桶演算法:
1)所有的請求在處理之前都需要拿到一個可用的令牌才會被處理;
2)根據限流大小,設定按照一定的速率往桶里添加令牌;
3)桶設定最大的放置令牌限制,當桶滿時、新添加的令牌就被丟棄或者拒絕;
4)請求達到后首先要獲取令牌桶中的令牌,拿著令牌才可以進行其他的業務邏輯,處理完業務邏輯之后,將令牌直接洗掉;
5)令牌桶有最低限額,當桶中的令牌達到最低限額的時候,請求處理完之后將不會洗掉令牌,以此保證足夠的限流
漏桶演算法的實作,有很多技術,Guaua是其中之一,redis客戶端也有其實作,
3.5.2 限流案例
spring cloud gateway 默認使用redis的RateLimter限流算法來實作,外面來簡要實作一下:
1、引入依賴
首先需要引入redis的依賴:
<!--redis-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis-reactive</artifactId>
<version>2.2.1.RELEASE</version>
</dependency>
同時不要忘記Redis配置:
redis:
host: 127.0.0.1
port: 6379
2、定義KeyResolver
在Application引導類中添加如下代碼,KeyResolver用于計算某一個型別的限流的KEY也就是說,可以通過KeyResolver來指定限流的Key,
我們可以根據IP來限流,比如每個IP每秒鐘只能請求一次,在GatewayApplication定義key的獲取,獲取客戶端IP,將IP作為key,如下代碼:
/***
* IP限流
* @return
*/
@Bean(name="ipKeyResolver")
public KeyResolver userKeyResolver() {
return new KeyResolver() {
@Override
public Mono<String> resolve(ServerWebExchange exchange) {
//獲取遠程客戶端IP
String hostName = exchange.getRequest().getRemoteAddress().getAddress().getHostAddress();
System.out.println("hostName:"+hostName);
return Mono.just(hostName);
}
};
}
在路由中配置如下:
gateway:
#路由配置
routes:
#唯一識別符號
- id: hailtaxi-driver
uri: lb://hailtaxi-driver
#路由斷言
predicates:
- Path=/driver/**
- Cookie=username,itheima
- Header=token,^(?!\d+$)[\da-zA-Z]+$
- Method=GET,POST
- Token=Authorization
filters:
- PayMethod=alipay,業務整合
- name: RequestRateLimiter #請求數限流 名字不能隨便寫 ,使用默認的facatory
args:
key-resolver: "#{@ipKeyResolver}"
redis-rate-limiter.replenishRate: 1
redis-rate-limiter.burstCapacity: 1
引數說明:
redis-rate-limiter.replenishRate是您希望允許用戶每秒執行多少請求,而不會丟棄任何請求,這是令牌桶填充的速率
redis-rate-limiter.burstCapacity是指令牌桶的容量,允許在一秒鐘內完成的最大請求數,將此值設定為零將阻止所有請求,
key-resolver: “#{@ipKeyResolver}” 用于通過SPEL運算式來指定使用哪一個KeyResolver.
如上配置:
表示 一秒內,允許 一個請求通過,令牌桶的填充速率也是一秒鐘添加一個令牌,
最大突發狀況 也只允許 一秒內有一次請求,可以根據業務來調整 ,
我們請求http://localhost:8001/driver/info/1?token=aa執行測驗,效果如下:

本文由傳智教育博學谷 - 狂野架構師教研團隊發布
如果本文對您有幫助,歡迎關注和點贊;如果您有任何建議也可留言評論或私信,您的支持是我堅持創作的動力
轉載請注明出處!
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/498680.html
標籤:Java
上一篇:封裝繼承多型
