閱讀本文前可先參考
??????SpringCloud - Spring Cloud根/父項(xiàng)目,開發(fā)準(zhǔn)備(二)_MinggeQingchun的博客-CSDN博客
SpringCloud - Spring Cloud 之 Gateway網(wǎng)關(guān)(十三)_MinggeQingchun的博客-CSDN博客
Web 有三大組件(監(jiān)聽器 過濾器 servlet),Spring Cloud GateWay 最主要的功能就是路由轉(zhuǎn)發(fā),而在定義轉(zhuǎn)發(fā)規(guī)則時(shí)主要涉及了以下三個(gè)核心概念
1、Route(路由)
2、Predicate(謂詞/斷言)
3、Filter(過濾)
一、Routes路由配置?
路由斷言/謂詞工廠有12個(gè)
Spring Cloud Gateway
Gateway有兩種配置路由方式
1、Java代碼配置類路由
參考官網(wǎng)給出demo?Spring Cloud Gateway
@SpringBootApplication
public class DemogatewayApplication {
@Bean
public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
return builder.routes()
.route("path_route", r -> r.path("/get")
.uri("http://httpbin.org"))
.route("host_route", r -> r.host("*.myhost.org")
.uri("http://httpbin.org"))
.route("rewrite_route", r -> r.host("*.rewrite.org")
.filters(f -> f.rewritePath("/foo/(?<segment>.*)", "/${segment}"))
.uri("http://httpbin.org"))
.route("hystrix_route", r -> r.host("*.hystrix.org")
.filters(f -> f.hystrix(c -> c.setName("slowcmd")))
.uri("http://httpbin.org"))
.route("hystrix_fallback_route", r -> r.host("*.hystrixfallback.org")
.filters(f -> f.hystrix(c -> c.setName("slowcmd").setFallbackUri("forward:/hystrixfallback")))
.uri("http://httpbin.org"))
.route("limit_route", r -> r
.host("*.limited.org").and().path("/anything/**")
.filters(f -> f.requestRateLimiter(c -> c.setRateLimiter(redisRateLimiter())))
.uri("http://httpbin.org"))
.build();
}
}
?在 springcloud-7-service-eureka-gateway 模塊總 自定義一個(gè)配置類?GatewayRouteConfig
import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* 路由配置類
*/
@Configuration
public class GatewayRouteConfig {
/**
* 代碼的路由 和 application.yml文件 不沖突,都可以用
* 如果 uri后面給了一個(gè)訪問地址 和匹配地址相同,就不會(huì)再拼接
*/
@Bean
public RouteLocator customRoiteLocator(RouteLocatorBuilder builder){
//以下url從B站中引用
return builder.routes()
.route("movie-id",r->r.path("/movie").uri("https://www.bilibili.com/movie/?spm_id_from=333.1007.0.0"))
.route("douga-id",r->r.path("/v/douga").uri("https://www.bilibili.com/v/douga/?spm_id_from=333.1007.0.0"))
.build();
}
}
啟動(dòng)springboot 類?
輸入訪問?http://localhost:81/movie
即會(huì)跳轉(zhuǎn)到相應(yīng)?url 進(jìn)行訪問資源
2、application.properties 或?application.yml 配置文件
即?SpringCloud - Spring Cloud 之 Gateway網(wǎng)關(guān)(十三)_MinggeQingchun的博客-CSDN博客
文中的 三、Gateway應(yīng)用方式?
application.properties
server.port=81
#eureka注冊中心首頁的Application這一欄
spring.application.name=springcloud-7-service-eureka-gateway
#每間隔5s,向Eureka服務(wù)注冊中心發(fā)送一次心跳,證明服務(wù)是否依然“存活”
eureka.instance.lease-renewal-interval-in-seconds=2
#告訴服務(wù)端,如果10s之內(nèi)沒有發(fā)送心跳,就代表故障,將本服務(wù)踢出
eureka.instance.lease-expiration-duration-in-seconds=10
#告訴服務(wù)端,服務(wù)實(shí)例以IP作為鏈接,不是取機(jī)器名
eureka.instance.prefer-ip-address=false
#注冊服務(wù)實(shí)例ID,,服務(wù)ID必須唯一 springcloud-7-service-eureka-gateway
eureka.instance.instance-id=${spring.application.name}:${server.port}
#注冊中心的鏈接地址 http://eureka8761:8761/eureka,http://eureka8762:8762/eureka,http://eureka8763:8763/eureka
eureka.client.service-url.defaultZone=http://localhost:8761/eureka
#網(wǎng)關(guān)路由配置
#開啟網(wǎng)關(guān),默認(rèn)開啟
spring.cloud.gateway.enabled=true
#節(jié)點(diǎn) routes 是一個(gè)List 對象,其中 routes 集合中中又包含多個(gè)對象,每個(gè)對象有三個(gè)屬性(一個(gè) 索引[0]代表一個(gè)對象)
#路由 id,沒有固定規(guī)則,但唯一
spring.cloud.gateway.routes[0].id=login-service-route
#匹配后提供服務(wù)的路由地址;uri統(tǒng)一資源定位符 url 統(tǒng)一資源標(biāo)識(shí)符
spring.cloud.gateway.routes[0].uri=http://localhost:9001
#以下是斷言條件,必選全部符合條件;斷言是給某一個(gè)路由來設(shè)定的一種匹配規(guī)則 默認(rèn)不能作用在動(dòng)態(tài)路由上
#斷言,路徑匹配,只要Path匹配上了/doLogin 就往 uri 轉(zhuǎn)發(fā) 并且將路徑帶上 注意:Path 中 P 為大寫
#也可以全局匹配,如 /service/**
spring.cloud.gateway.routes[0].predicates[0]=Path=/doLogin
#只能是 GET 請求時(shí),才能訪問
spring.cloud.gateway.routes[0].predicates[0]=Method=GET,POST
#配置第二個(gè)路由規(guī)則
spring.cloud.gateway.routes[1].id=admin-service-route
spring.cloud.gateway.routes[1].uri=http://localhost:9001
spring.cloud.gateway.routes[1].predicates[0]=Path=/doAdmin
spring.cloud.gateway.routes[1].predicates[0]=Method=GET,POST
#表明gateway開啟服務(wù)注冊和發(fā)現(xiàn)的功能,并且spring cloud gateway自動(dòng)根據(jù)服務(wù)發(fā)現(xiàn)為每一個(gè)服務(wù)創(chuàng)建了一個(gè)router,這個(gè)router將以服務(wù)名開頭的請求路徑轉(zhuǎn)發(fā)到對應(yīng)的服務(wù)
spring.cloud.gateway.discovery.locator.enabled=true
#是將請求路徑上的服務(wù)名配置為小寫(服務(wù)注冊的時(shí)候,向注冊中心注冊時(shí)將服務(wù)名轉(zhuǎn)成大寫了),如以/service/*的請求路徑被路由轉(zhuǎn)發(fā)到服務(wù)名為service的服務(wù)上
spring.cloud.gateway.discovery.locator.lower-case-service-id=true
application.yml?
server:
port: 81
#eureka注冊中心首頁的Application這一欄
spring:
application:
name: springcloud-7-service-eureka-gateway
#網(wǎng)關(guān)路由配置
cloud:
gateway:
#開啟網(wǎng)關(guān),默認(rèn)開啟
enabled: true
#節(jié)點(diǎn) routes 是一個(gè)List 對象,其中 routes 集合中中又包含多個(gè)對象,每個(gè)對象有三個(gè)屬性(一個(gè) 索引[0]代表一個(gè)對象)
routes:
#路由 id,沒有固定規(guī)則,但唯一
- id: login-service-route
#匹配后提供服務(wù)的路由地址;uri統(tǒng)一資源定位符 url 統(tǒng)一資源標(biāo)識(shí)符
uri: http://localhost:9001
#以下是斷言條件,必選全部符合條件;斷言是給某一個(gè)路由來設(shè)定的一種匹配規(guī)則 默認(rèn)不能作用在動(dòng)態(tài)路由上
predicates:
#斷言,路徑匹配,只要Path匹配上了/doLogin 就往 uri 轉(zhuǎn)發(fā) 并且將路徑帶上 注意:Path 中 P 為大寫
#也可以全局匹配,如 /service/**
- Path=/doLogin
#只能是 GET,POST 請求時(shí),才能訪問
- Method=GET,POST
#配置第二個(gè)路由規(guī)則
- id: admin-service-route
uri: http://localhost:9001
predicates:
- Path=/doAdmin
- Method=GET,POST
#表明gateway開啟服務(wù)注冊和發(fā)現(xiàn)的功能,并且spring cloud gateway自動(dòng)根據(jù)服務(wù)發(fā)現(xiàn)為每一個(gè)服務(wù)創(chuàng)建了一個(gè)router,這個(gè)router將以服務(wù)名開頭的請求路徑轉(zhuǎn)發(fā)到對應(yīng)的服務(wù)
discovery:
locator:
enabled: true
#是將請求路徑上的服務(wù)名配置為小寫(服務(wù)注冊的時(shí)候,向注冊中心注冊時(shí)將服務(wù)名轉(zhuǎn)成大寫了),如以/service/*的請求路徑被路由轉(zhuǎn)發(fā)到服務(wù)名為service的服務(wù)上
lower-case-service-id: true
eureka:
instance:
#每間隔5s,向Eureka服務(wù)注冊中心發(fā)送一次心跳,證明服務(wù)是否依然“存活”
lease-renewal-interval-in-seconds: 2
#告訴服務(wù)端,如果10s之內(nèi)沒有發(fā)送心跳,就代表故障,將本服務(wù)踢出
lease-expiration-duration-in-seconds: 10
#告訴服務(wù)端,服務(wù)實(shí)例以IP作為鏈接,不是取機(jī)器名
prefer-ip-address: false
#注冊服務(wù)實(shí)例ID,,服務(wù)ID必須唯一 springcloud-7-service-eureka-gateway
instance-id: ${spring.application.name}:${server.port}
#注冊中心的鏈接地址 http://eureka8761:8761/eureka,http://eureka8762:8762/eureka,http://eureka8763:8763/eureka
client:
service-url:
defaultZone: http://localhost:8761/eureka
3、Gateway 微服務(wù)名動(dòng)態(tài)路由,負(fù)載均衡?
我們設(shè)置的 routes的?uri?都是寫死的,這樣不符合微服務(wù)的要求,微服務(wù)是只要知道服務(wù)的名字,根據(jù)名字去找,且直接寫死URI就沒有負(fù)載均衡的效果
默認(rèn)情況下 Gateway 會(huì)根據(jù)注冊中心的服務(wù)列表,以注冊中心上微服務(wù)名為路徑創(chuàng)建動(dòng)態(tài)路
由進(jìn)行轉(zhuǎn)發(fā),從而實(shí)現(xiàn)動(dòng)態(tài)路由的功能
uri 的協(xié)議(如 http 協(xié)議)為 lb(load Balance),表示啟用 Gateway 的負(fù)載均衡功能。
lb://serviceName 是 spring cloud gateway 在微服務(wù)中自動(dòng)為我們創(chuàng)建的負(fù)載均衡 uri
#匹配后提供服務(wù)的路由地址;uri統(tǒng)一資源定位符 url 統(tǒng)一資源標(biāo)識(shí)符
#uri 的協(xié)議為 lb(load Balance),表示啟用 Gateway 的負(fù)載均衡功能
#lb://serviceName 是 spring cloud gateway 在微服務(wù)中自動(dòng)為我們創(chuàng)建的負(fù)載均衡 uri;serviceName要和啟動(dòng)的微服務(wù)名保持一致
spring.cloud.gateway.routes[0].uri=lb://springcloud-7-service-eureka-gateway-login
#eureka注冊中心首頁的Application這一欄
spring:
application:
name: springcloud-7-service-eureka-gateway
#網(wǎng)關(guān)路由配置
cloud:
gateway:
#開啟網(wǎng)關(guān),默認(rèn)開啟
enabled: true
#節(jié)點(diǎn) routes 是一個(gè)List 對象,其中 routes 集合中中又包含多個(gè)對象,每個(gè)對象有三個(gè)屬性(一個(gè) 索引[0]代表一個(gè)對象)
routes:
#路由 id,沒有固定規(guī)則,但唯一
- id: login-service-route
#匹配后提供服務(wù)的路由地址;uri統(tǒng)一資源定位符 url 統(tǒng)一資源標(biāo)識(shí)符
#uri: http://localhost:9001
#uri 的協(xié)議為 lb(load Balance),表示啟用 Gateway 的負(fù)載均衡功能
#lb://serviceName 是 spring cloud gateway 在微服務(wù)中自動(dòng)為我們創(chuàng)建的負(fù)載均衡 uri;serviceName要和啟動(dòng)的微服務(wù)名保持一致
uri: lb://springcloud-7-service-eureka-gateway-login
#以下是斷言條件,必選全部符合條件;斷言是給某一個(gè)路由來設(shè)定的一種匹配規(guī)則 默認(rèn)不能作用在動(dòng)態(tài)路由上
predicates:
#斷言,路徑匹配,只要Path匹配上了/doLogin 就往 uri 轉(zhuǎn)發(fā) 并且將路徑帶上 注意:Path 中 P 為大寫
#也可以全局匹配,如 /service/**
- Path=/doLogin
#只能是 GET,POST 請求時(shí),才能訪問
- Method=GET,POST
瀏覽器輸入訪問http://localhost:81/springcloud-7-service-eureka-gateway-login/doLogin
二、Predicate 謂詞/斷言
路由斷言/謂詞工廠有12個(gè)
Spring Cloud Gateway
??
Gateway 啟動(dòng)時(shí)會(huì)去加載一些路由斷言工廠(一個(gè) boolean 表達(dá)式 )
Spring Cloud Gateway
我們可以通過上述官網(wǎng)查看斷言表達(dá)式?
斷言就是路由添加一些條件(通俗的說,斷言就是一些布爾表達(dá)式,滿足條件的返回 true,不滿足的返回 false)
Spring Cloud Gateway 將路由作為 Spring WebFlux HandlerMapping 基礎(chǔ)架構(gòu)的一部分進(jìn)行匹配。
Spring Cloud Gateway 包括許多內(nèi)置的路由斷言工廠。所有這些斷言都與 HTTP請求的不同屬性匹配??梢詫?strong>多個(gè)路由斷言可以組合使用
Spring Cloud Gateway 創(chuàng)建對象時(shí),使用 RoutePredicateFactory 創(chuàng)建 Predicate 對象,Predicate 對象可以賦值給 Route。
規(guī)則 | 實(shí)例 | 說明 |
---|---|---|
Path | - Path=/gate/,/rule/ | ## 當(dāng)請求的路徑為gate、rule開頭的時(shí),轉(zhuǎn)發(fā)到http://localhost:9023服務(wù)器上 |
Before | - Before=2020-01-20T17:42:47.789-07:00[America/Denver] | 在某個(gè)時(shí)間之前的請求才會(huì)被轉(zhuǎn)發(fā)到 http://localhost:9023服務(wù)器上 |
After | - After=2020-01-20T17:42:47.789-07:00[America/Denver] | 在某個(gè)時(shí)間之后的請求才會(huì)被轉(zhuǎn)發(fā) |
Between | - Between=2020-01-20T17:42:47.789-07:00[America/Denver],2020-01-21T17:42:47.789-07:00[America/Denver] | 在某個(gè)時(shí)間段之間的才會(huì)被轉(zhuǎn)發(fā) |
Cookie | - Cookie=chocolate, ch.p | 名為chocolate的表單或者滿足正則ch.p的表單才會(huì)被匹配到進(jìn)行請求轉(zhuǎn)發(fā) |
Header | - Header=X-Request-Id, \d+ | 攜帶參數(shù)X-Request-Id或者滿足\d+的請求頭才會(huì)匹配 |
Host | - Host=www.hd123.com | 當(dāng)主機(jī)名為www.hd123.com的時(shí)候直接轉(zhuǎn)發(fā)到http://localhost:9023服務(wù)器上 |
Method | - Method=GET | 只有GET方法才會(huì)匹配轉(zhuǎn)發(fā)請求,還可以限定POST、PUT等請求方式 |
application.yml配置文件
server:
port: 81
spring:
application:
name: gateway-81
cloud:
gateway:
enabled: true #開啟網(wǎng)關(guān),默認(rèn)是開啟的
routes: #設(shè)置路由,注意是數(shù)組,可以設(shè)置多個(gè),按照 id 做隔離
- id: user-service #路由 id,沒有要求,保持唯一即可
uri: lb://provider #使用 lb 協(xié)議 微服務(wù)名稱做負(fù)均衡
predicates: #斷言匹配
- Path=/info/** #和服務(wù)中的路徑匹配,是正則匹配的模式
- After=2020-01-20T17:42:47.789-07:00[Asia/Shanghai] #此斷言匹配發(fā)生在指定 日期時(shí)間之后的請求,ZonedDateTime dateTime=ZonedDateTime.now()獲得
- Before=2020-06-18T21:26:26.711+08:00[Asia/Shanghai] #此斷言匹配發(fā)生在指定 日期時(shí)間之前的請求
- Between=2020-06-18T21:26:26.711+08:00[Asia/Shanghai],2020-06-18T21:32:26.711+08:00[Asia/Shanghai] #此斷言匹配發(fā)生在指定日期時(shí)間之間的請求
- Cookie=name,xiaobai #Cookie 路由斷言工廠接受兩個(gè)參數(shù),Cookie 名稱和 regexp(一 個(gè) Java 正則表達(dá)式)。此斷言匹配具有給定名稱且其值與正則表達(dá)式匹配的 cookie
- Header=token,123456 #頭路由斷言工廠接受兩個(gè)參數(shù),頭名稱和 regexp(一個(gè) Java 正 則表達(dá)式)。此斷言與具有給定名稱的頭匹配,該頭的值與正則表達(dá)式匹配。
- Host=**.bai*.com:* #主機(jī)路由斷言工廠接受一個(gè)參數(shù):主機(jī)名模式列表。該模式是一 個(gè) ant 樣式的模式。作為分隔符。此斷言匹配與模式匹配的主機(jī)頭
- Method=GET,POST #方法路由斷言工廠接受一個(gè)方法參數(shù),該參數(shù)是一個(gè)或多個(gè)參數(shù): 要匹配的 HTTP 方法
- Query=username,cxs #查詢路由斷言工廠接受兩個(gè)參數(shù):一個(gè)必需的 param 和一個(gè) 可選的 regexp(一個(gè) Java 正則表達(dá)式)。
- RemoteAddr=192.168.1.1/24 #RemoteAddr 路由斷言工廠接受一個(gè)源列表(最小大小 1), 這些源是 cidr 符號(hào)(IPv4 或 IPv6)字符串,比如 192.168.1.1/24(其中 192.168.1.1 是 IP 地址,24 是子網(wǎng)掩碼)。
1、After路由謂詞工廠
After route謂詞工廠采用一個(gè)參數(shù),即datetime(這是一個(gè)Java ZonedDateTime),該謂詞匹配在指定日期時(shí)間之后發(fā)生的請求,以下示例配置了路由后謂詞:
spring:
cloud:
gateway:
routes:
- id: after_route
uri: https://example.org
predicates:
- After=2017-01-20T17:42:47.789-07:00[America/Denver]
這條路由符合2017年1月20日17:42:47時(shí)間([America/Denver])之后的任何請求;
時(shí)間通過獲?。?/span>System.out.println(ZonedDateTime.now());
2、Before路由謂詞工廠
Before路由謂詞工廠采用一個(gè)參數(shù),即datetime(這是一個(gè)Java ZonedDateTime),該謂詞匹配在指定日期時(shí)間之前發(fā)生的請求,下面的示例配置路由之前謂詞:
spring:
cloud:
gateway:
routes:
- id: before_route
uri: https://example.org
predicates:
- Before=2017-01-20T17:42:47.789-07:00[America/Denver]
這條路由符合2017年1月20日17:42:47時(shí)間([America/Denver])之前的任何請求;
3、Between路由謂詞工廠
路由謂詞之間的工廠使用兩個(gè)參數(shù)datetime1和datetime2,它們是java ZonedDateTime對象,該謂詞匹配在datetime1之后和datetime2之前發(fā)生的請求,datetime2參數(shù)必須在datetime1之后,以下示例配置了路由之間的謂詞:
spring:
cloud:
gateway:
routes:
- id: between_route
uri: https://example.org
predicates:
- Between=2017-01-20T17:42:47.789-07:00[America/Denver], 2017-01-21T17:42:47.789-07:00[America/Denver]
該路線與2017年1月20日山區(qū)時(shí)間(丹佛)之后和2017年1月21日17:42山區(qū)時(shí)間(丹佛)之前的任何請求相匹配,這對于維護(hù)時(shí)段可能很有用;
4、Cookie 路由謂詞工廠
Cookie路由謂詞工廠采用兩個(gè)參數(shù),即cookie名稱和一個(gè)regexp(這是Java正則表達(dá)式),該謂詞匹配具有給定名稱且其值與正則表達(dá)式匹配的cookie,以下示例配置Cookie路由謂詞工廠:
spring:
cloud:
gateway:
routes:
- id: cookie_route
uri: https://example.org
predicates:
- Cookie=chocolate, ch.p
此路由匹配具有名為Chocolate的cookie的請求,該cookie的值與ch.p正則表達(dá)式匹配;
舉例:curl http://192.168.0.104/index --cookie token=123456
5、Header 路由謂詞工廠
header 路由謂詞工廠使用兩個(gè)參數(shù),header 名稱和一個(gè)regexp(這是Java正則表達(dá)式),該謂詞與具有給定名稱的header 匹配,該header 的值與正則表達(dá)式匹配,以下示例配置標(biāo)頭路由謂詞:
spring:
cloud:
gateway:
routes:
- id: header_route
uri: https://example.org
predicates:
- Header=X-Request-Id, \d+
如果請求具有名為X-Request-Id的標(biāo)頭,且其值與\ d +正則表達(dá)式匹配(即,其值為一個(gè)或多個(gè)數(shù)字),則此路由匹配;
舉例:curl http://192.168.0.104/index --header "X-Request-Id:19228"
6、Host 路由謂詞工廠
host路由謂詞工廠使用一個(gè)參數(shù):主機(jī)名模式列表,以下示例配置主機(jī)路由謂詞:
spring:
cloud:
gateway:
routes:
- id: host_route
uri: https://example.org
predicates:
- Host=**.somehost.org,**.anotherhost.org
還支持URI模板變量(例如{sub} .myhost.org),如果請求的主機(jī)標(biāo)頭的值為www.somehost.org或beta.somehost.org或www.anotherhost.org,則此路由匹配;
7、Method 路由謂詞工廠
方法路由謂詞工廠使用方法參數(shù),該參數(shù)是一個(gè)或多個(gè)參數(shù):要匹配的HTTP方法,以下示例配置方法route謂詞:
spring:
cloud:
gateway:
routes:
- id: method_route
uri: https://example.org
predicates:
- Method=GET,POST
如果請求方法是GET或POST,則此路由匹配;
8、Path路由謂詞工廠
路徑路由謂詞工廠使用兩個(gè)參數(shù):Spring PathMatcher模式列表和一個(gè)稱為matchOptionalTrailingSeparator的可選標(biāo)志,以下示例配置路徑路由謂詞:
spring:
cloud:
gateway:
routes:
- id: path_route
uri: https://example.org
predicates:
- Path=/red/{segment},/blue/{segment}
如果請求路徑為例如/red/1或/red/blue或/blue/green,則此路由匹配;
9、Query路由謂詞工廠
查詢路由謂詞工廠采用兩個(gè)參數(shù):必需的參數(shù)和可選的regexp(這是Java正則表達(dá)式),以下示例配置查詢路由謂詞:
spring:
cloud:
gateway:
routes:
- id: query_route
uri: https://example.org
predicates:
- Query=green
如果請求包含green查詢參數(shù),則前面的路由匹配;
spring:
cloud:
gateway:
routes:
- id: query_route
uri: https://example.org
predicates:
- Query=red, gree.
如果請求包含值與gree匹配的red查詢參數(shù),則上述路由匹配;
10、RemoteAddr 路由謂詞工廠
RemoteAddr路由謂詞工廠使用源列表(最小大小為1),這些源是標(biāo)記(IPv4或IPv6)字符串,例如192.168.0.1/16(其中192.168.0.1是IP地址,而16是子網(wǎng)掩碼)),下面的示例配置RemoteAddr路由謂詞:
spring:
cloud:
gateway:
routes:
- id: remoteaddr_route
uri: https://example.org
predicates:
- RemoteAddr=192.168.1.1/24
如果請求的遠(yuǎn)程地址是例如192.168.1.10,則此路由匹配;
11、Weight 路由謂詞工廠
權(quán)重路由謂詞工廠采用兩個(gè)參數(shù):group和weight(一個(gè)int),權(quán)重是按組計(jì)算的,以下示例配置權(quán)重路由謂詞:
spring:
cloud:
gateway:
routes:
- id: weight_high
uri: https://weighthigh.org
predicates:
- Weight=group1, 8
- id: weight_low
uri: https://weightlow.org
predicates:
- Weight=group1, 2
這條路由會(huì)將約80%的流量轉(zhuǎn)發(fā)至weighthigh.org,并將約20%的流量轉(zhuǎn)發(fā)至weightlow.org;
12、XForwarded?路由謂詞工廠
spring:
cloud:
gateway:
routes:
- id: xforwarded_remoteaddr_route
uri: https://example.org
predicates:
- XForwardedRemoteAddr=192.168.1.1/24
如果x - forward - for報(bào)頭包含,例如192.168.1.10,則此路由匹配
一、自定義謂詞/斷言
1、首先定義一個(gè)配置類,用于承載配置參數(shù)
@Data //lombok
public class TokenConfig {
private String token;
}
2、定義一個(gè)路由謂詞工廠
@Slf4j
@Component
public class TokenRoutePredicateFactory extends AbstractRoutePredicateFactory<TokenConfig> {
public TokenRoutePredicateFactory() {
super(TokenConfig.class);
}
@Override
public List<String> shortcutFieldOrder() {
return Collections.singletonList("token");
}
@Override
public Predicate<ServerWebExchange> apply(TokenConfig tokenConfig) {
// (T t) -> true
return exchange -> {
MultiValueMap<String, String> valueMap = exchange.getRequest().getQueryParams();
boolean flag = false;
List<String> list = new ArrayList<>();
valueMap.forEach((k, v) -> {
list.addAll(v);
});
for (String s : list) {
log.info("Token -> {} ", s);
if (StringUtils.equalsIgnoreCase(s, tokenConfig.getToken())) {
flag = true;
break;
}
}
return flag;
};
}
}
注:TokenRoutePredicateFactory類,前面的Token與.yml配置文件里面配置的名字對應(yīng),后面的RoutePredicateFactory名字是固定的,不能隨便寫,這是Spring Cloud Gateway的約定,類名須為“謂詞工廠名(如:Token)” + RoutePredicateFactory
3、在配置文件中啟用該路由謂詞工廠,即配置一個(gè)Token=123456
時(shí)間格式不是隨便配置,而是Spring Cloud Gateway的默認(rèn)時(shí)間格式,采用JDK8里面的格式化:
DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofLocalizedTime(FormatStyle.SHORT);
String nowTime = dateTimeFormatter.format(ZonedDateTime.now());
System.out.println(nowTime);
二、謂詞/斷言不匹配404處理
處理的頂層接口是WebExceptionHandler
默認(rèn)實(shí)現(xiàn)是DefaultErrorWebExceptionHandler
需要覆蓋它的默認(rèn)實(shí)現(xiàn)DefaultErrorWebExceptionHandler,覆蓋里面的方法,在方法里面編寫返回的結(jié)果
import org.springframework.boot.autoconfigure.web.ErrorProperties;
import org.springframework.boot.autoconfigure.web.ResourceProperties;
import org.springframework.boot.autoconfigure.web.reactive.error.DefaultErrorWebExceptionHandler;
import org.springframework.boot.web.reactive.error.ErrorAttributes;
import org.springframework.context.ApplicationContext;
import org.springframework.web.reactive.function.server.*;
import java.util.HashMap;
import java.util.Map;
public class MyErrorWebExceptionHandler extends DefaultErrorWebExceptionHandler {
public MyErrorWebExceptionHandler(ErrorAttributes errorAttributes,
ResourceProperties resourceProperties,
ErrorProperties errorProperties,
ApplicationContext applicationContext) {
super(errorAttributes, resourceProperties, errorProperties, applicationContext);
}
/**
* 獲取異常屬性
*/
/*@Override
protected Map<String, Object> getErrorAttributes(ServerRequest request, boolean includeStackTrace) {
int code = 500;
Throwable error = super.getError(request);
if (error instanceof org.springframework.cloud.gateway.support.NotFoundException) {
code = 404;
}
return response(code, this.buildMessage(request, error));
}*/
/**
* 指定響應(yīng)處理方法為JSON處理的方法
*
* @param errorAttributes
*/
@Override
protected RouterFunction<ServerResponse> getRoutingFunction(ErrorAttributes errorAttributes) {
return RouterFunctions.route(RequestPredicates.all(), this::renderErrorResponse);
}
/**
* 根據(jù)code獲取對應(yīng)的HttpStatus
* @param errorAttributes
*/
@Override
protected int getHttpStatus(Map<String, Object> errorAttributes) {
int statusCode = (int) errorAttributes.get("status");
return statusCode;
}
/**
* 構(gòu)建異常信息
* @param request
* @param ex
* @return
*/
private String buildMessage(ServerRequest request, Throwable ex) {
StringBuilder message = new StringBuilder("Failed to handle request [");
message.append(request.methodName());
message.append(" ");
message.append(request.uri());
message.append("]");
if (ex != null) {
message.append(": ");
message.append(ex.getMessage());
}
return message.toString();
}
/**
* 構(gòu)建返回的JSON數(shù)據(jù)格式
*
* @param status 狀態(tài)碼
* @param errorMessage 異常信息
* @return
*/
public static Map<String, Object> response(int status, String errorMessage) {
Map<String, Object> map = new HashMap<>();
map.put("code", status);
map.put("message", errorMessage);
map.put("data", null);
return map;
}
}
三、Filter 過濾器
Filter有33個(gè)
Spring Cloud Gateway
Gateway 里面的過濾器和 Servlet 里面的過濾器,功能差不多,路由過濾器可以用于修改進(jìn)入
Http 請求和返回 Http 響應(yīng)
Gateway 中過濾器
1、按生命周期
pre 在業(yè)務(wù)邏輯之前
post 在業(yè)務(wù)邏輯之后
2、按種類
GatewayFilter 需要配置某個(gè)路由,才能過濾。如果需要使用全局路由,需要配置 DefaultFilters
GlobalFilter 全局過濾器,不需要配置路由,系統(tǒng)初始化作用到所有路由上
全局過濾器 統(tǒng)計(jì)請求次數(shù);限流;token 的校驗(yàn);ip 黑名單攔截 ;跨域本質(zhì)(filter) ;144 開頭的電話;限制一些 ip 的訪問
1、自定義?GlobalFilter 全局過濾器
GlobalFilter 是一種作用于所有的路由上的全局過濾器,通過它,我們可以實(shí)現(xiàn)一些統(tǒng)一化的業(yè)務(wù)功能,例如權(quán)限認(rèn)證、IP 訪問限制等。當(dāng)某個(gè)請求被路由匹配時(shí),那么所有的 GlobalFilter 會(huì)和該路由自身配置的 GatewayFilter 組合成一個(gè)過濾器鏈
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import java.util.HashMap;
/**
* 自定義全局過濾器
*/
@Component
public class GlobalFilterConfig implements GlobalFilter, Ordered {
/**
* 過濾方法
* 過濾器鏈模式;責(zé)任鏈模式
*/
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
//針對請求的過濾,拿到請求的header、url、參數(shù)等
// HttpServletRequest 是web里面的
// ServerHttpRequest 是webFlux里面(響應(yīng)式)
ServerHttpRequest request = exchange.getRequest();
String path = request.getURI().getPath();
System.out.println("path====" + path);
HttpHeaders headers = request.getHeaders();
System.out.println("headers====" + headers);
String methodName = request.getMethod().name();
System.out.println("methodName====" + methodName);
//IPV4、IPV6地址
String hostName = request.getRemoteAddress().getHostName();
System.out.println("hostName====" + hostName);
String ip = request.getHeaders().getHost().getHostString();
System.out.println("ip====" + ip);
// 響應(yīng)相關(guān)的數(shù)據(jù)
ServerHttpResponse response = exchange.getResponse();
//響應(yīng)頭設(shè)置編碼
response.getHeaders().set("content-type","application/json;charset=utf-8");
HashMap<String ,Object> map = new HashMap<>(4);
map.put("code", HttpStatus.UNAUTHORIZED.value());
map.put("msg","你未授權(quán)");
ObjectMapper objectMapper = new ObjectMapper();
//將一個(gè)map轉(zhuǎn)成一個(gè)字節(jié)數(shù)組
byte[] bytes = new byte[0];
try {
bytes = objectMapper.writeValueAsBytes(map);
} catch (JsonProcessingException e) {
e.printStackTrace();
}
//通過buffer工廠將字節(jié)數(shù)組包裝成一個(gè)數(shù)據(jù)包
DataBuffer wrap = response.bufferFactory().wrap(bytes);
return response.writeWith(Mono.just(wrap));
// 放行 到下一個(gè)過濾器
// return chain.filter(exchange);
}
/**
* 指定順序的方法,越小越先執(zhí)行
*/
@Override
public int getOrder() {
return 0;
}
}
2、IP、Token認(rèn)證攔截
1、token認(rèn)證攔截
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
/**
* token校驗(yàn)
*/
@Component
public class TokenCheckFilter implements GlobalFilter, Ordered {
/**
* 指定好放行的路徑
*/
public static final List<String> ALLOW_URL = Arrays.asList("/springcloud-7-service-eureka-gateway-login/doLogin", "/myUrl","/doLogin");
@Autowired
private StringRedisTemplate redisTemplate;
/**
* 和前端約定好 一般放在請求頭里面 一般 key Authorization value bearer token
* 1、拿到請求url
* 2、判斷放行
* 3、拿到請求頭
* 4、拿到token
* 5、校驗(yàn)
* 6、放行/攔截
*/
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
String path = request.getURI().getPath();
if (ALLOW_URL.contains(path)) {
return chain.filter(exchange);
}
// 檢查
HttpHeaders headers = request.getHeaders();
List<String> authorization = headers.get("Authorization");
if (!CollectionUtils.isEmpty(authorization)) {
String token = authorization.get(0);
if (StringUtils.hasText(token)) {
// 約定好的有前綴的 bearer token
String realToken = token.replaceFirst("bearer ", "");
if (StringUtils.hasText(realToken) && redisTemplate.hasKey(realToken)) {
return chain.filter(exchange);
}
}
}
// 攔截
ServerHttpResponse response = exchange.getResponse();
response.getHeaders().set("content-type","application/json;charset=utf-8");
HashMap<String, Object> map = new HashMap<>(4);
// 返回401
map.put("code", HttpStatus.UNAUTHORIZED.value());
map.put("msg","未授權(quán)");
ObjectMapper objectMapper = new ObjectMapper();
byte[] bytes = new byte[0];
try {
bytes = objectMapper.writeValueAsBytes(map);
} catch (JsonProcessingException e) {
e.printStackTrace();
}
DataBuffer wrap = response.bufferFactory().wrap(bytes);
return response.writeWith(Mono.just(wrap));
}
/**
* 這個(gè)順序 最好在0附近 -2 -1 0 1
* @return
*/
@Override
public int getOrder() {
return 1;
}
2、IP認(rèn)證攔截
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
/**
* 網(wǎng)關(guān)里面 過濾器
* ip攔截
* 請求都有一個(gè)源頭
* 電話 如:144 開頭
* 請求------->gateway------->service
* 黑名單 black_list
* 白名單 white_list
* 根據(jù)數(shù)量
* 像具體的業(yè)務(wù)服務(wù) 一般黑名單
* 一般像數(shù)據(jù)庫 用白名單
*/
@Component
public class IPCheckFilter implements GlobalFilter, Ordered {
/**
* 網(wǎng)關(guān)的并發(fā)比較高,不要在網(wǎng)關(guān)里面直接操作mysql
* 后臺(tái)系統(tǒng)可以查詢數(shù)據(jù)庫,用戶量并發(fā)量不大
* 如果并發(fā)量大 可以查redis 或者 在內(nèi)存中寫好
*/
public static final List<String> BLACK_LIST = Arrays.asList("127.0.0.1", "144.128.139.137");
/**
* 1、獲取到ip
* 2、校驗(yàn)ip是否符合規(guī)范
* 3、放行 OR 攔截
*/
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
String ip = request.getHeaders().getHost().getHostString();
// 查詢數(shù)據(jù)庫 看這個(gè)ip是否存在黑名單里面
if (!BLACK_LIST.contains(ip)){
return chain.filter(exchange);
}
//攔截
//注:此處如果攔截IP,就要放開 GlobalFilter 的攔截
ServerHttpResponse response = exchange.getResponse();
response.getHeaders().set("content-type","application/json;charset=utf-8");
HashMap<String, Object> map = new HashMap<>(4);
map.put("code", 438);
map.put("msg","你是黑名單");
ObjectMapper objectMapper = new ObjectMapper();
byte[] bytes = new byte[0];
try {
bytes = objectMapper.writeValueAsBytes(map);
} catch (JsonProcessingException e) {
e.printStackTrace();
}
DataBuffer wrap = response.bufferFactory().wrap(bytes);
return response.writeWith(Mono.just(wrap));
}
@Override
public int getOrder() {
return 0;
}
}
3、限流
限流就是限制一段時(shí)間內(nèi),用戶訪問資源的次數(shù),減輕服務(wù)器壓力,限流大致分為兩種:
1、IP 限流(5s 內(nèi)同一個(gè) ip 訪問超過 3 次,則限制不讓訪問,過一段時(shí)間才可繼續(xù)訪問)
2.、請求量限流(只要在一段時(shí)間內(nèi)(窗口期),請求次數(shù)達(dá)到閥值,就直接拒絕后面來的訪問了,
過一段時(shí)間才可以繼續(xù)訪問)(粒度可以細(xì)化到一個(gè) api(url),一個(gè)服務(wù))
限流模型:漏斗算法 ,令牌桶算法,窗口滑動(dòng)算法 計(jì)數(shù)器算法
令牌桶算法
(1)所有的請求在處理之前都需要拿到一個(gè)可用的令牌才會(huì)被處理;
(2)根據(jù)限流大小,設(shè)置按照一定的速率往桶里添加令牌
(3)桶設(shè)置最大的放置令牌限制,當(dāng)桶滿時(shí)、新添加的令牌就被丟棄或者拒絕;
(4)請求達(dá)到后首先要獲取令牌桶中的令牌,拿著令牌才可以進(jìn)行其他的業(yè)務(wù)邏輯,處理完業(yè)務(wù)邏輯之后,將令牌直接刪除;
(5)令牌桶有最低限額,當(dāng)桶中的令牌達(dá)到最低限額的時(shí)候,請求處理完之后將不會(huì)刪除令
牌,以此保證足夠的限流
Spring Cloud Gateway 已經(jīng)內(nèi)置了一個(gè) RequestRateLimiterGatewayFilterFactory,可以直接使用。
目前 RequestRateLimiterGatewayFilterFactory 的實(shí)現(xiàn)依賴于 Redis,所以我們還要引入 spring-boot-starter-data-redis-reactive 依賴
<!--限流要引入 Redis-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis-reactive</artifactId>
</dependency>
配置類
import org.springframework.cloud.gateway.filter.ratelimit.KeyResolver;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import reactor.core.publisher.Mono;
/**
* 自定義請求限流
*/
@Configuration
public class RequestRateLimiterConfig {
// 針對某一個(gè)接口,ip來限流,如/doLogin,每一個(gè)ip,10s只能訪問3次
@Bean
@Primary // 主候選的
public KeyResolver ipKeyResolver() {
return exchange -> Mono.just(exchange.getRequest().getHeaders().getHost().getHostString());
}
// 針對這個(gè)路徑來限制 /doLogin
// api 就是 接口 外面一般把gateway api網(wǎng)關(guān) 新一代網(wǎng)關(guān)
@Bean
public KeyResolver apiKeyResolver() {
return exchange -> Mono.just(exchange.getRequest().getPath().value());
}
}
需要添加一個(gè)? @Primary // 主候選的 注解,不然報(bào)如下錯(cuò)誤
啟動(dòng)快速訪問?
4、CORS跨域配置
@Configuration
public class CorsConfig {
@Bean
public CorsWebFilter corsFilter() {
CorsConfiguration config = new CorsConfiguration();
config.addAllowedMethod("*");
config.addAllowedOrigin("*");
config.addAllowedHeader("*");
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(new PathPatternParser());
source.registerCorsConfiguration("/**", config);
return new CorsWebFilter(source);
}
}
spring:
cloud:
gateway:
globalcors:
corsConfigurations: '[/**]': // 針對哪些路徑
allowCredentials: true // 這個(gè)是可以攜帶 cookie
allowedHeaders: '*'
allowedMethods: '*' allowedOrigins: '*'
四、Spring Cloud Gateway集成ribbon負(fù)載均衡
實(shí)現(xiàn)原理是在全局LoadBalancerClientFilter中進(jìn)行攔截,然后再該過濾器中依賴LoadBalancerClient loadBalancer,而此負(fù)載均衡接口的具體實(shí)現(xiàn)是RibbonLoadBalancerClient,即spring cloud gateway已經(jīng)整合好了ribbon,已經(jīng)可以實(shí)現(xiàn)負(fù)載均衡,我們不需要做任何工作,網(wǎng)關(guān)對后端微服務(wù)的轉(zhuǎn)發(fā)就已經(jīng)具有負(fù)載均衡功能;
1、添加依賴
<!-- sentinel-spring-cloud-gateway-adapter -->
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-spring-cloud-gateway-adapter</artifactId>
<version>1.7.2</version>
</dependency>
2、添加sentinel控制臺(tái)配置文章來源:http://www.zghlxwxcb.cn/news/detail-621385.html
#sentinel dashboard管理后臺(tái)
spring:
cloud:
sentinel:
eager: true
transport:
dashboard: 192.168.133.128:8080
3、在spring容器中配置一個(gè)Sentinel的全局過濾器文章來源地址http://www.zghlxwxcb.cn/news/detail-621385.html
@Bean
@Order(-1)
public GlobalFilter sentinelGatewayFilter() {
return new SentinelGatewayFilter();
}
到了這里,關(guān)于SpringCloud - Spring Cloud 之 Gateway網(wǎng)關(guān),Route路由,Predicate 謂詞/斷言,F(xiàn)ilter 過濾器(十三)的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!