Springcloud之Gateway組件詳解
1.網(wǎng)關(guān)
1.1 網(wǎng)關(guān)簡(jiǎn)介
大家都都知道在微服務(wù)架構(gòu)中,一個(gè)系統(tǒng)會(huì)被拆分為很多個(gè)微服務(wù)。那么作為客戶端要如何去調(diào)用這么多的微服務(wù)呢?如果沒有網(wǎng)關(guān)的存在,我們只能在客戶端記錄每個(gè)微服務(wù)的地址,然后分別去調(diào)用。
這樣的架構(gòu),會(huì)存在著諸多的問題:
1.客戶端多次請(qǐng)求不同的微服務(wù),增加客戶端代碼或配置編寫的復(fù)雜性
2.認(rèn)證復(fù)雜,每個(gè)服務(wù)都需要獨(dú)立認(rèn)證。
3.存在跨域請(qǐng)求,在一定場(chǎng)景下處理相對(duì)復(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)的公共邏輯可以在這里實(shí)現(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ù)載均衡可實(shí)現(xiàn)對(duì)api服務(wù)器的負(fù)載均衡及高可用,lua是一種腳本語言,可以來編寫一些簡(jiǎn)單的邏輯, nginx支持lua腳本
Kong
基于Nginx+Lua開發(fā),性能高,穩(wěn)定,有多個(gè)可用的插件(限流、鑒權(quán)等等)可以開箱即用。
問題:只支持Http協(xié)議;二次開發(fā),自由擴(kuò)展困難;提供管理API,缺乏更易用的管控、配置方式。Zuul 1.0
Netflix開源的網(wǎng)關(guān),功能豐富,使用JAVA開發(fā),易于二次開發(fā)
問題:缺乏管控,無法動(dòng)態(tài)配置;依賴組件較多;處理Http請(qǐng)求依賴的是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)提供一種簡(jiǎn)單有效的統(tǒng)一的APl路由管理方式。它的目標(biāo)是替代Netflix Zuul,其不僅提供統(tǒng)一的路由方式,并且基于Filter鏈的方式提供了網(wǎng)關(guān)基本的功能,例如:安全,監(jiān)控和限流。
優(yōu)點(diǎn):
1·性能強(qiáng)勁:是第—代網(wǎng)關(guān)Zuul的1.6倍。
2·功能強(qiáng)大:內(nèi)置了很多實(shí)用的功能,例如轉(zhuǎn)發(fā)、監(jiān)控、限流等。
3.設(shè)計(jì)優(yōu)雅,容易擴(kuò)展。
缺點(diǎn):
1.其實(shí)現(xiàn)依賴Netty與WebFlux,不是傳統(tǒng)的Servlet編程模型,學(xué)習(xí)成本高。
2.不能將其部署在Tomcat、Jetty等Servlet容器里,只能打成jar包執(zhí)行。
3.需要Spring Boot 2.0及以上的版本,才支持。
1.2.2 Gateway實(shí)踐
這里模擬通過瀏覽器訪問網(wǎng)關(guān),然后網(wǎng)關(guān)將請(qǐng)求轉(zhuǎn)發(fā)到訂單微服務(wù),通過查詢訂單返回訂單信息,在其中過程中訂單中有用戶信息,需要根據(jù)訂單中的用戶id調(diào)用用戶微服務(wù)進(jìn)行查詢?nèi)缓筮M(jìn)行賦值然后返回訂單信息。
新建一個(gè)網(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)請(qǐng)求滿足什么樣的條件轉(zhuǎn)發(fā)到哪個(gè)微服務(wù)上
- id: order_router #當(dāng)前路由標(biāo)識(shí),要求唯一
uri: lb://provider #請(qǐng)求最終要被轉(zhuǎn)發(fā)的地址,lb指的是從nacos中按照名稱獲取微服務(wù),并按照負(fù)載均衡策略
order: 1 #路由的優(yōu)先級(jí),數(shù)字越小代表路由的優(yōu)先級(jí)越高
predicates: #斷言(條件判斷,返回值是boolean,轉(zhuǎn)發(fā)請(qǐng)求要滿足的條件)
- Path=/orderservice/**
filters:
- StripPrefix=1 #在請(qǐng)求轉(zhuǎn)發(fā)之前去掉一層路徑,這里去掉orderservice
discovery:
locator:
enabled: true #讓gateway可以發(fā)現(xiàn)nacos中的微服務(wù),這個(gè)必須配置首先在運(yùn)行我們的項(xiàng)目的時(shí)候訪問訂單微服務(wù)http://localhost:9200/order/2就可以獲得訂單信息,通過上述配置后我們通過網(wǎng)關(guān)訪問http://localhost:7000/orderservice/order/2也可以獲得訂單信息,然后證明我們配置成功。其實(shí)在gateway中有一層默認(rèn)的設(shè)置,當(dāng)我們把yml中配置的路由全部注釋掉后然后訪問http://localhost:7000/provider/order/2依然可以獲得訂單信息,其中provider為訂單微服務(wù)的名稱,默認(rèn)設(shè)置和我們的配置相對(duì)比就是把orderservice換成了provider微服務(wù)名稱。
1.2.3 Gateway執(zhí)行流程

執(zhí)行流程大體如下:
1.Gateway Client向Gateway Server發(fā)送請(qǐng)求
⒉.請(qǐng)求首先會(huì)被HttpWebHandlerAdapter進(jìn)行提取組裝成網(wǎng)關(guān)上下文
3.然后網(wǎng)關(guān)的上下文會(huì)傳遞到DispatcherHandler,它負(fù)責(zé)將請(qǐng)求分發(fā)給RoutePredicateHandlerMapping
4.RoutePredicateHandlerMapping負(fù)責(zé)路由查找,并根據(jù)路由斷言判斷路由是否可用
5.如果過斷言成功,由FilteringWebHandler創(chuàng)建過濾器鏈并調(diào)用
6.請(qǐng)求會(huì)一次經(jīng)過PreFilter--微服務(wù)--PostFilter的方法,最終返回響應(yīng)
1.2.4 斷言工廠
這里摘用網(wǎng)上的相關(guān)內(nèi)置斷言工廠的描述
基于時(shí)間DateTime類型的斷言工廠
此類型的斷言根據(jù)時(shí)間做判斷,主要有三個(gè):
AfterRoutePredicateFactory:接收一個(gè)日期參數(shù),判斷請(qǐng)求日期是否晚于指定日期BeforeRoutePredicateFactory:接收一個(gè)日期參數(shù),判斷請(qǐng)求日期是否早于指定日期BetweenRoutePredicateFactory:接收兩個(gè)日期參數(shù),判斷請(qǐng)求日期是否在指定時(shí)間段內(nèi)
# 當(dāng)前的請(qǐng)求必須要在下方指定的時(shí)間之后 - After=2017-01-20T17:42:47.789-07:00[America/Denver] # 當(dāng)前的請(qǐng)求必須在下方指定的時(shí)間之前 - Before=2017-01-20T17:42:47.789-07:00[America/Denver] # 當(dāng)前的請(qǐng)求必須在下方指定的時(shí)間段之內(nèi) - Between=2017-01-20T17:42:47.789-07:00[America/Denver],2017-01-21T17:42:47.789-07:00[America/Denver]
基于Cookie的斷言工廠
判斷請(qǐng)求Cookie中必須某個(gè)key對(duì)應(yīng)的value必須為指定的值
# cookie中chocolate的值必須為ch.p 第二個(gè)參數(shù)的值可以使用正則表達(dá)式 - Cookie=chocolate,ch.p
基于請(qǐng)求頭的斷言工廠
根據(jù)請(qǐng)求頭中的某一個(gè)參數(shù)來進(jìn)行匹配
# 必須包含X-Request-Id請(qǐng)求頭,第二個(gè)參數(shù)的值可以使用正則表達(dá)式 - Header=X-Request-Id,\d+
基于域名的斷言工廠
多個(gè)域名使用逗號(hào)分隔,支持通配符
- Host=**.somehost.org,**.anotherhost.org
基于請(qǐng)求方法的斷言工廠
支持GET請(qǐng)求或者POST請(qǐng)求,中間使用逗號(hào)分隔
- Method=GET,POST
基于Path路徑的斷言工廠
也就是我們快速入門案例中使用的,也支持通配符,如果有多個(gè)path中間使用逗號(hào)分隔
- Path=/order_server/**
除了通配符的方式,它還支持占位符的方式就比如rest接口中在結(jié)尾攜帶一個(gè)請(qǐng)求參數(shù)的方式,就比如/get/user/{id},這個(gè)id在路由匹配中肯定不能寫死,
- Path=/red/{segment},/blue/{segment}如果請(qǐng)求路徑是,則此路由匹配,例如:/red/1 or /red/1/ 或 /red/blue or /blue/green。
Path Route Predicate Factory 有兩個(gè)參數(shù):一個(gè) Spring 列表PathMatcher patterns和一個(gè)名為matchTrailingSlash(默認(rèn)為true)的可選標(biāo)志。如果matchTrailingSlash設(shè)置為false,則請(qǐng)求路徑/red/1/將不匹配。
基于請(qǐng)求參數(shù)的斷言工廠
根據(jù)請(qǐng)求參數(shù)中必須包含某個(gè)請(qǐng)求參數(shù),或者某個(gè)請(qǐng)求參數(shù)的值是XXX,這個(gè)請(qǐng)求參數(shù)只能滿足GET請(qǐng)求的方式在URL后面進(jìn)行?key=value拼接的方式
# 請(qǐng)求參數(shù)中包含green這個(gè)參數(shù),則與前面的路由進(jìn)行匹配 - Query=green # 如果請(qǐng)求參數(shù)包含red其值與正則gree.表達(dá)式匹配的查詢參數(shù),則前面的路由匹配,因此red=green red=greet都會(huì)會(huì)匹配。 - Query=red,gree.
根據(jù)請(qǐng)求客戶端Ip的斷言工廠
例如,如果請(qǐng)求的遠(yuǎn)程地址是192.168.1.10 ,則此路由匹配。
- RemoteAddr=192.168.1.1/24
根據(jù)權(quán)重
權(quán)重通常會(huì)有多個(gè)路由規(guī)則來組成,多個(gè)路由之間進(jìn)行一個(gè)百分比的權(quán)重
權(quán)重需要配置兩個(gè)參數(shù),第一個(gè)參數(shù)是組名,第二個(gè)是權(quán)重值,多個(gè)路由如果是在一組中那么組名需要相同
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
自定義斷言工廠實(shí)現(xiàn)
首先在配置文件中添加如下,表示參數(shù)age年齡的范圍,在這之間符合要求。
gateway:
routes: #路由數(shù)組,指當(dāng)請(qǐng)求滿足什么樣的條件轉(zhuǎn)發(fā)到哪個(gè)微服務(wù)上
- id: order_router #當(dāng)前路由標(biāo)識(shí),要求唯一
uri: lb://provider #請(qǐng)求最終要被轉(zhuǎn)發(fā)的地址,lb指的是從nacos中按照名稱獲取微服務(wù),并按照負(fù)載均衡策略
order: 1 #路由的優(yōu)先級(jí),數(shù)字越小代表路由的優(yōu)先級(jí)越高
predicates: #斷言(條件判斷,返回值是boolean,轉(zhuǎn)發(fā)請(qǐng)求要滿足的條件)
- Path=/orderservice/**
- Age=18,60
filters:
- StripPrefix=1自定義代碼實(shí)現(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");//這個(gè)位置的順序必須和配置文件中的順序?qū)?yīng)
}
//用于接收配置文件中的對(duì)應(yīng)參數(shù)
@Data
@NoArgsConstructor
public static class Config{
private int minAge;
private int maxAge;
}
}1.2.5 過濾器
過濾器就是對(duì)于請(qǐng)求和響應(yīng)做一些手腳。作用流程如下圖:

從過濾器生命周期(影響時(shí)機(jī)點(diǎn))的?度來說,主要有兩個(gè)pre和post:
pre:這種過濾器在請(qǐng)求被路由之前調(diào)用。我們可以利用這類過濾器實(shí)現(xiàn)身份驗(yàn)證、在集群中選擇 請(qǐng)求的微服務(wù)、記錄調(diào)試信息等。
post:這種過濾器在路由到微服務(wù)以后執(zhí)行。這類過濾器可用來為響應(yīng)添加標(biāo)準(zhǔn)的HTTP Header、收集統(tǒng)計(jì)信息和指標(biāo)、將響應(yīng)從微服務(wù)發(fā)送給客戶端。
從過濾器作用范圍的角度來說,可分為另外兩種,一種是針對(duì)于單個(gè)路由的gateway filter,它在配置文件中的寫法同predict類似;另外一種是針對(duì)于所有路由的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能夠識(shí)別的過濾器。
內(nèi)置局部過濾器

全局過濾器
內(nèi)置的全局過濾器如下圖:

自定義全局過濾器
內(nèi)置的過濾器已經(jīng)可以完成大部分的功能,但是對(duì)于企業(yè)開發(fā)的一些業(yè)務(wù)功能處理,還是需要我們自己編寫過濾器來實(shí)現(xiàn)的,那么我們一起通過代碼的形式自定義一個(gè)過濾器,去完成統(tǒng)一的權(quán)限校驗(yàn)。
開發(fā)中的鑒權(quán)邏輯:
1.當(dāng)客戶端第一次請(qǐng)求服務(wù)時(shí),服務(wù)端對(duì)用戶進(jìn)行信息認(rèn)證(登錄)
2.認(rèn)證通過,將用戶信息進(jìn)行加密形成token,返回給客戶端,作為登錄憑證·以后每次請(qǐng)求,客戶端都攜帶認(rèn)證的token
3.服務(wù)端對(duì)token進(jìn)行解密,判斷是否有效。
如下圖:

自定義代碼如下
@Component
@Order(-1)//越小優(yōu)先級(jí)越高
@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組件的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Redisson分布式閉鎖RCountDownLatch的使用詳細(xì)講解
分布式鎖和我們java基礎(chǔ)中學(xué)習(xí)到的synchronized略有不同,synchronized中我們的鎖是個(gè)對(duì)象,當(dāng)前系統(tǒng)部署在不同的服務(wù)實(shí)例上,單純使用synchronized或者lock已經(jīng)無法滿足對(duì)庫(kù)存一致性的判斷。本次主要講解基于rediss實(shí)現(xiàn)的分布式鎖2023-02-02
spring boot國(guó)際化之MessageSource的使用方法
這篇文章主要給大家介紹了spring boot國(guó)際化之MessageSource使用的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-11-11
Java類的初始化順序知識(shí)點(diǎn)總結(jié)
在本篇文章里小編給大家整理的是關(guān)于Java類的初始化順序知識(shí)點(diǎn)總結(jié),需要的朋友們可以學(xué)習(xí)下。2020-02-02
Java double轉(zhuǎn)BigDecimal的注意事項(xiàng)說明
這篇文章主要介紹了Java double轉(zhuǎn)BigDecimal的注意事項(xiàng)說明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2021-01-01
Java數(shù)據(jù)結(jié)構(gòu)之鏈表的增刪查改詳解
在這篇文章中,小編將帶大家了解一下Java數(shù)據(jù)結(jié)構(gòu)中鏈表的增刪查改(以下結(jié)果均在IDEA中編譯)希望在方便自己復(fù)習(xí)的同時(shí)也能幫助到大家2022-09-09
JAVA多線程與并發(fā)學(xué)習(xí)總結(jié)分析
以下是對(duì)小編對(duì)JAVA多線程與并發(fā)的學(xué)習(xí)進(jìn)行了總結(jié)介紹,需要的朋友可以過來參考下2013-08-08
RabbitMQ消息隊(duì)列的目錄結(jié)構(gòu)
這篇文章主要介紹了RabbitMQ消息隊(duì)列的目錄結(jié)構(gòu),RabbitMQ?屬于消息中間件,主要用于組件之間的解耦,消息的發(fā)送者無需知道消息使用者的存在,反之亦然,那么用了那么久RabbitMQ,其目錄結(jié)構(gòu)是怎樣的呢,讓我們一起來看一下吧2023-08-08

