Springcloud之Gateway組件詳解
1.網(wǎng)關(guān)
1.1 網(wǎng)關(guān)簡介
大家都都知道在微服務(wù)架構(gòu)中,一個系統(tǒng)會被拆分為很多個微服務(wù)。那么作為客戶端要如何去調(diào)用這么多的微服務(wù)呢?如果沒有網(wǎng)關(guān)的存在,我們只能在客戶端記錄每個微服務(wù)的地址,然后分別去調(diào)用。
這樣的架構(gòu),會存在著諸多的問題:
1.客戶端多次請求不同的微服務(wù),增加客戶端代碼或配置編寫的復(fù)雜性
2.認(rèn)證復(fù)雜,每個服務(wù)都需要獨立認(rèn)證。
3.存在跨域請求,在一定場景下處理相對復(fù)雜。
上面的這些問題可以借助API網(wǎng)關(guān)來解決。所謂的API網(wǎng)關(guān),就是指系統(tǒng)的統(tǒng)一入口,它封裝了應(yīng)用程序的內(nèi)部結(jié)構(gòu),為客戶端提供統(tǒng)一服務(wù),一些與業(yè)務(wù)本身功能無關(guān)的公共邏輯可以在這里實現(xiàn),諸如認(rèn)證、鑒權(quán)、監(jiān)控、路由轉(zhuǎn)發(fā)等等。架構(gòu)如下圖所示:
1.2 網(wǎng)關(guān)組件
在業(yè)界比較流行的網(wǎng)關(guān),有下面這些:
Ngnix+lua
使用nginx的反向代理和負(fù)載均衡可實現(xiàn)對api服務(wù)器的負(fù)載均衡及高可用,lua是一種腳本語言,可以來編寫一些簡單的邏輯, nginx支持lua腳本
Kong
基于Nginx+Lua開發(fā),性能高,穩(wěn)定,有多個可用的插件(限流、鑒權(quán)等等)可以開箱即用。
問題:只支持Http協(xié)議;二次開發(fā),自由擴(kuò)展困難;提供管理API,缺乏更易用的管控、配置方式。Zuul 1.0
Netflix開源的網(wǎng)關(guān),功能豐富,使用JAVA開發(fā),易于二次開發(fā)
問題:缺乏管控,無法動態(tài)配置;依賴組件較多;處理Http請求依賴的是Web容器。
SpringCloud Gateway
Spring公司為了替換Zuul而開發(fā)的網(wǎng)關(guān)服務(wù),將在下面具體介紹。
1.2.1 Gateway介紹
Spring Cloud Gateway是Spring公司基于Spring 5.0,Spring Boot 2.0和Project Reactor等技術(shù)開發(fā)的網(wǎng)關(guān),它旨在為微服務(wù)架構(gòu)提供一種簡單有效的統(tǒng)一的APl路由管理方式。它的目標(biāo)是替代Netflix Zuul,其不僅提供統(tǒng)一的路由方式,并且基于Filter鏈的方式提供了網(wǎng)關(guān)基本的功能,例如:安全,監(jiān)控和限流。
優(yōu)點:
1·性能強(qiáng)勁:是第—代網(wǎng)關(guān)Zuul的1.6倍。
2·功能強(qiáng)大:內(nèi)置了很多實用的功能,例如轉(zhuǎn)發(fā)、監(jiān)控、限流等。
3.設(shè)計優(yōu)雅,容易擴(kuò)展。
缺點:
1.其實現(xiàn)依賴Netty與WebFlux,不是傳統(tǒng)的Servlet編程模型,學(xué)習(xí)成本高。
2.不能將其部署在Tomcat、Jetty等Servlet容器里,只能打成jar包執(zhí)行。
3.需要Spring Boot 2.0及以上的版本,才支持。
1.2.2 Gateway實踐
這里模擬通過瀏覽器訪問網(wǎng)關(guān),然后網(wǎng)關(guān)將請求轉(zhuǎn)發(fā)到訂單微服務(wù),通過查詢訂單返回訂單信息,在其中過程中訂單中有用戶信息,需要根據(jù)訂單中的用戶id調(diào)用用戶微服務(wù)進(jìn)行查詢?nèi)缓筮M(jìn)行賦值然后返回訂單信息。
新建一個網(wǎng)關(guān)微服務(wù)
導(dǎo)入依賴
//注意導(dǎo)入了gateway網(wǎng)關(guān)不要導(dǎo)入Spring-boot-starter-web依賴,有沖突 <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>
配置application.yml
server: port: 7000 spring: rabbitmq: username: admin password: admin host: 182.92.167.13 port: 5672 application: name: gateway cloud: nacos: discovery: server-addr: localhost:8848 gateway: routes: #路由數(shù)組,指當(dāng)請求滿足什么樣的條件轉(zhuǎn)發(fā)到哪個微服務(wù)上 - id: order_router #當(dāng)前路由標(biāo)識,要求唯一 uri: lb://provider #請求最終要被轉(zhuǎn)發(fā)的地址,lb指的是從nacos中按照名稱獲取微服務(wù),并按照負(fù)載均衡策略 order: 1 #路由的優(yōu)先級,數(shù)字越小代表路由的優(yōu)先級越高 predicates: #斷言(條件判斷,返回值是boolean,轉(zhuǎn)發(fā)請求要滿足的條件) - Path=/orderservice/** filters: - StripPrefix=1 #在請求轉(zhuǎn)發(fā)之前去掉一層路徑,這里去掉orderservice discovery: locator: enabled: true #讓gateway可以發(fā)現(xiàn)nacos中的微服務(wù),這個必須配置
首先在運行我們的項目的時候訪問訂單微服務(wù)http://localhost:9200/order/2就可以獲得訂單信息,通過上述配置后我們通過網(wǎng)關(guān)訪問http://localhost:7000/orderservice/order/2也可以獲得訂單信息,然后證明我們配置成功。其實在gateway中有一層默認(rèn)的設(shè)置,當(dāng)我們把yml中配置的路由全部注釋掉后然后訪問http://localhost:7000/provider/order/2依然可以獲得訂單信息,其中provider為訂單微服務(wù)的名稱,默認(rèn)設(shè)置和我們的配置相對比就是把orderservice換成了provider微服務(wù)名稱。
1.2.3 Gateway執(zhí)行流程
執(zhí)行流程大體如下:
1.Gateway Client向Gateway Server發(fā)送請求
⒉.請求首先會被HttpWebHandlerAdapter進(jìn)行提取組裝成網(wǎng)關(guān)上下文
3.然后網(wǎng)關(guān)的上下文會傳遞到DispatcherHandler,它負(fù)責(zé)將請求分發(fā)給RoutePredicateHandlerMapping
4.RoutePredicateHandlerMapping負(fù)責(zé)路由查找,并根據(jù)路由斷言判斷路由是否可用
5.如果過斷言成功,由FilteringWebHandler創(chuàng)建過濾器鏈并調(diào)用
6.請求會一次經(jīng)過PreFilter--微服務(wù)--PostFilter的方法,最終返回響應(yīng)
1.2.4 斷言工廠
這里摘用網(wǎng)上的相關(guān)內(nèi)置斷言工廠的描述
基于時間DateTime類型的斷言工廠
此類型的斷言根據(jù)時間做判斷,主要有三個:
AfterRoutePredicateFactory:接收一個日期參數(shù),判斷請求日期是否晚于指定日期BeforeRoutePredicateFactory:接收一個日期參數(shù),判斷請求日期是否早于指定日期BetweenRoutePredicateFactory:接收兩個日期參數(shù),判斷請求日期是否在指定時間段內(nèi)
# 當(dāng)前的請求必須要在下方指定的時間之后 - After=2017-01-20T17:42:47.789-07:00[America/Denver] # 當(dāng)前的請求必須在下方指定的時間之前 - Before=2017-01-20T17:42:47.789-07:00[America/Denver] # 當(dāng)前的請求必須在下方指定的時間段之內(nèi) - Between=2017-01-20T17:42:47.789-07:00[America/Denver],2017-01-21T17:42:47.789-07:00[America/Denver]
基于Cookie的斷言工廠
判斷請求Cookie中必須某個key對應(yīng)的value必須為指定的值
# cookie中chocolate的值必須為ch.p 第二個參數(shù)的值可以使用正則表達(dá)式 - Cookie=chocolate,ch.p
基于請求頭的斷言工廠
根據(jù)請求頭中的某一個參數(shù)來進(jìn)行匹配
# 必須包含X-Request-Id請求頭,第二個參數(shù)的值可以使用正則表達(dá)式 - Header=X-Request-Id,\d+
基于域名的斷言工廠
多個域名使用逗號分隔,支持通配符
- Host=**.somehost.org,**.anotherhost.org
基于請求方法的斷言工廠
支持GET請求或者POST請求,中間使用逗號分隔
- Method=GET,POST
基于Path路徑的斷言工廠
也就是我們快速入門案例中使用的,也支持通配符,如果有多個path中間使用逗號分隔
- Path=/order_server/**
除了通配符的方式,它還支持占位符的方式就比如rest接口中在結(jié)尾攜帶一個請求參數(shù)的方式,就比如/get/user/{id}
,這個id在路由匹配中肯定不能寫死,
- Path=/red/{segment},/blue/{segment}
如果請求路徑是,則此路由匹配,例如:/red/1 or /red/1/ 或 /red/blue or /blue/green。
Path Route Predicate Factory 有兩個參數(shù):一個 Spring 列表PathMatcher patterns和一個名為matchTrailingSlash(默認(rèn)為true)的可選標(biāo)志。如果matchTrailingSlash設(shè)置為false,則請求路徑/red/1/將不匹配。
基于請求參數(shù)的斷言工廠
根據(jù)請求參數(shù)中必須包含某個請求參數(shù),或者某個請求參數(shù)的值是XXX,這個請求參數(shù)只能滿足GET請求的方式在URL后面進(jìn)行?key=value
拼接的方式
# 請求參數(shù)中包含green這個參數(shù),則與前面的路由進(jìn)行匹配 - Query=green # 如果請求參數(shù)包含red其值與正則gree.表達(dá)式匹配的查詢參數(shù),則前面的路由匹配,因此red=green red=greet都會會匹配。 - Query=red,gree.
根據(jù)請求客戶端Ip的斷言工廠
例如,如果請求的遠(yuǎn)程地址是192.168.1.10
,則此路由匹配。
- RemoteAddr=192.168.1.1/24
根據(jù)權(quán)重
權(quán)重通常會有多個路由規(guī)則來組成,多個路由之間進(jìn)行一個百分比的權(quán)重
權(quán)重需要配置兩個參數(shù),第一個參數(shù)是組名,第二個是權(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
該路由會將約 80% 的流量轉(zhuǎn)發(fā)到weighthigh.org,將約 20% 的流量轉(zhuǎn)發(fā)到weightlow.org
自定義斷言工廠實現(xiàn)
首先在配置文件中添加如下,表示參數(shù)age年齡的范圍,在這之間符合要求。
gateway: routes: #路由數(shù)組,指當(dāng)請求滿足什么樣的條件轉(zhuǎn)發(fā)到哪個微服務(wù)上 - id: order_router #當(dāng)前路由標(biāo)識,要求唯一 uri: lb://provider #請求最終要被轉(zhuǎn)發(fā)的地址,lb指的是從nacos中按照名稱獲取微服務(wù),并按照負(fù)載均衡策略 order: 1 #路由的優(yōu)先級,數(shù)字越小代表路由的優(yōu)先級越高 predicates: #斷言(條件判斷,返回值是boolean,轉(zhuǎn)發(fā)請求要滿足的條件) - Path=/orderservice/** - Age=18,60 filters: - StripPrefix=1
自定義代碼實現(xiàn)如下
@Component public class AgeRoutePredicateFactory extends AbstractRoutePredicateFactory<AgeRoutePredicateFactory.Config> { //斷言邏輯 public Predicate<ServerWebExchange> apply(AgeRoutePredicateFactory.Config config) { return new Predicate<ServerWebExchange>() { @Override public boolean test(ServerWebExchange serverWebExchange) { String age = serverWebExchange.getRequest().getQueryParams().getFirst("age"); if(age!=null){ int myage = Integer.parseInt(age); if(myage>config.minAge&&myage<config.maxAge) return true; else return false; } return false; } }; } public AgeRoutePredicateFactory() { super(AgeRoutePredicateFactory.Config.class); } //讀取配置文件中的參數(shù)值,賦值到配置類上的屬性上 public List<String> shortcutFieldOrder() { return Arrays.asList("minAge","maxAge");//這個位置的順序必須和配置文件中的順序?qū)?yīng) } //用于接收配置文件中的對應(yīng)參數(shù) @Data @NoArgsConstructor public static class Config{ private int minAge; private int maxAge; } }
1.2.5 過濾器
過濾器就是對于請求和響應(yīng)做一些手腳。作用流程如下圖:
從過濾器生命周期(影響時機(jī)點)的?度來說,主要有兩個pre和post:
pre:這種過濾器在請求被路由之前調(diào)用。我們可以利用這類過濾器實現(xiàn)身份驗證、在集群中選擇 請求的微服務(wù)、記錄調(diào)試信息等。
post:這種過濾器在路由到微服務(wù)以后執(zhí)行。這類過濾器可用來為響應(yīng)添加標(biāo)準(zhǔn)的HTTP Header、收集統(tǒng)計信息和指標(biāo)、將響應(yīng)從微服務(wù)發(fā)送給客戶端。
從過濾器作用范圍的角度來說,可分為另外兩種,一種是針對于單個路由的gateway filter,它在配置文件中的寫法同predict類似;另外一種是針對于所有路由的global gateway filer?,F(xiàn)在從作用范圍劃分的維度來講解這兩種filter。
區(qū)別:
GatewayFilter:網(wǎng)關(guān)過濾器,需要通過spring.cloud.routes.filters配置在具體的路由下,只作用在當(dāng)前特定路由上,也可以通過配置spring.cloud.default-filters讓它作用于全局路由上。
GlobalFilter:全局過濾器,不需要再配置文件中配置,作用在所有的路由上,最終通過GatewayFilterAdapter包裝成GatewayFilterChain能夠識別的過濾器。
內(nèi)置局部過濾器
全局過濾器
內(nèi)置的全局過濾器如下圖:
自定義全局過濾器
內(nèi)置的過濾器已經(jīng)可以完成大部分的功能,但是對于企業(yè)開發(fā)的一些業(yè)務(wù)功能處理,還是需要我們自己編寫過濾器來實現(xiàn)的,那么我們一起通過代碼的形式自定義一個過濾器,去完成統(tǒng)一的權(quán)限校驗。
開發(fā)中的鑒權(quán)邏輯:
1.當(dāng)客戶端第一次請求服務(wù)時,服務(wù)端對用戶進(jìn)行信息認(rèn)證(登錄)
2.認(rèn)證通過,將用戶信息進(jìn)行加密形成token,返回給客戶端,作為登錄憑證·以后每次請求,客戶端都攜帶認(rèn)證的token
3.服務(wù)端對token進(jìn)行解密,判斷是否有效。
如下圖:
自定義代碼如下
@Component @Order(-1)//越小優(yōu)先級越高 @Slf4j public class Filter implements GlobalFilter { public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { String token = exchange.getRequest().getQueryParams().getFirst("token"); if(token==null||!token.equals("admin")){ //認(rèn)證失敗 log.error("認(rèn)證失敗"); exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED); return exchange.getResponse().setComplete(); } return chain.filter(exchange); } }
以上就是Springcloud之Gateway組件詳解的詳細(xì)內(nèi)容,更多關(guān)于Springcloud Gateway組件的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Redisson分布式閉鎖RCountDownLatch的使用詳細(xì)講解
分布式鎖和我們java基礎(chǔ)中學(xué)習(xí)到的synchronized略有不同,synchronized中我們的鎖是個對象,當(dāng)前系統(tǒng)部署在不同的服務(wù)實例上,單純使用synchronized或者lock已經(jīng)無法滿足對庫存一致性的判斷。本次主要講解基于rediss實現(xiàn)的分布式鎖2023-02-02spring boot國際化之MessageSource的使用方法
這篇文章主要給大家介紹了spring boot國際化之MessageSource使用的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-11-11Java double轉(zhuǎn)BigDecimal的注意事項說明
這篇文章主要介紹了Java double轉(zhuǎn)BigDecimal的注意事項說明,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2021-01-01Java數(shù)據(jù)結(jié)構(gòu)之鏈表的增刪查改詳解
在這篇文章中,小編將帶大家了解一下Java數(shù)據(jù)結(jié)構(gòu)中鏈表的增刪查改(以下結(jié)果均在IDEA中編譯)希望在方便自己復(fù)習(xí)的同時也能幫助到大家2022-09-09JAVA多線程與并發(fā)學(xué)習(xí)總結(jié)分析
以下是對小編對JAVA多線程與并發(fā)的學(xué)習(xí)進(jìn)行了總結(jié)介紹,需要的朋友可以過來參考下2013-08-08