一区二区三区三上|欧美在线视频五区|国产午夜无码在线观看视频|亚洲国产裸体网站|无码成年人影视|亚洲AV亚洲AV|成人开心激情五月|欧美性爱内射视频|超碰人人干人人上|一区二区无码三区亚洲人区久久精品

0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

Spring Cloud Gateway服務(wù)網(wǎng)關(guān)的部署與使用詳細(xì)教程

jf_ro2CN3Fa ? 來源:張維鵬 ? 作者:張維鵬 ? 2022-10-11 17:46 ? 次閱讀

一、為什么需要服務(wù)網(wǎng)關(guān):

1、什么是服務(wù)網(wǎng)關(guān):

2、服務(wù)網(wǎng)關(guān)的基本功能:

3、流量網(wǎng)關(guān)與服務(wù)網(wǎng)關(guān)的區(qū)別:

二、服務(wù)網(wǎng)關(guān)的部署:

1、主流網(wǎng)關(guān)的對(duì)比與選型:

2、Spring Cloud Gateway 網(wǎng)關(guān)的搭建:

3、Spring Cloud Gateway 配置項(xiàng)的說明:

4、Gateway 集成 nacos 注冊中心實(shí)現(xiàn)服務(wù)發(fā)現(xiàn):

5、Gateway 整合 Apollo 實(shí)現(xiàn)動(dòng)態(tài)路由配置:

6、自定義全局異常處理器

一、為什么需要服務(wù)網(wǎng)關(guān):

1、什么是服務(wù)網(wǎng)關(guān):

傳統(tǒng)的單體架構(gòu)中只需要開放一個(gè)服務(wù)給客戶端調(diào)用,但是微服務(wù)架構(gòu)中是將一個(gè)系統(tǒng)拆分成多個(gè)微服務(wù),如果沒有網(wǎng)關(guān),客戶端只能在本地記錄每個(gè)微服務(wù)的調(diào)用地址,當(dāng)需要調(diào)用的微服務(wù)數(shù)量很多時(shí),它需要了解每個(gè)服務(wù)的接口,這個(gè)工作量很大。那有了網(wǎng)關(guān)之后,能夠起到怎樣的改善呢?

網(wǎng)關(guān)作為系統(tǒng)的唯一流量入口,封裝內(nèi)部系統(tǒng)的架構(gòu),所有請(qǐng)求都先經(jīng)過網(wǎng)關(guān),由網(wǎng)關(guān)將請(qǐng)求路由到合適的微服務(wù),所以,使用網(wǎng)關(guān)的好處在于:

簡化客戶端的工作。 網(wǎng)關(guān)將微服務(wù)封裝起來后,客戶端只需同網(wǎng)關(guān)交互,而不必調(diào)用各個(gè)不同服務(wù);

降低函數(shù)間的耦合度。 一旦服務(wù)接口修改,只需修改網(wǎng)關(guān)的路由策略,不必修改每個(gè)調(diào)用該函數(shù)的客戶端,從而減少了程序間的耦合性

解放開發(fā)人員把精力專注于業(yè)務(wù)邏輯的實(shí)現(xiàn)。 由網(wǎng)關(guān)統(tǒng)一實(shí)現(xiàn)服務(wù)路由(灰度與ABTest)、負(fù)載均衡、訪問控制、流控熔斷降級(jí)等非業(yè)務(wù)相關(guān)功能,而不需要每個(gè)服務(wù) API 實(shí)現(xiàn)時(shí)都去考慮

但是 API 網(wǎng)關(guān)也存在不足之處,在微服務(wù)這種去中心化的架構(gòu)中,網(wǎng)關(guān)又成了一個(gè)中心點(diǎn)或瓶頸點(diǎn),它增加了一個(gè)我們必須開發(fā)、部署和維護(hù)的高可用組件。正是由于這個(gè)原因,在網(wǎng)關(guān)設(shè)計(jì)時(shí)必須考慮即使 API 網(wǎng)關(guān)宕機(jī)也不要影響到服務(wù)的調(diào)用和運(yùn)行,所以需要對(duì)網(wǎng)關(guān)的響應(yīng)結(jié)果有數(shù)據(jù)緩存能力,通過返回緩存數(shù)據(jù)或默認(rèn)數(shù)據(jù)屏蔽后端服務(wù)的失敗。

在服務(wù)的調(diào)用方式上面,網(wǎng)關(guān)也有一定的要求,API 網(wǎng)關(guān)最好是支持 I/O 異步、同步非阻塞的,如果服務(wù)是同步阻塞調(diào)用,可以理解為微服務(wù)模塊之間是沒有徹底解耦的,即如果A依賴B提供的API,如果B提供的服務(wù)不可用將直接影響到A不可用,除非同步服務(wù)調(diào)用在API網(wǎng)關(guān)層或客戶端做了相應(yīng)的緩存。

因此為了徹底解耦,在微服務(wù)調(diào)用上更建議選擇異步方式進(jìn)行。而對(duì)于 API 網(wǎng)關(guān)需要通過底層多個(gè)細(xì)粒度的 API 組合的場景,推薦采用響應(yīng)式編程模型進(jìn)行而不是傳統(tǒng)的異步回調(diào)方法組合代碼,其原因除了采用回調(diào)方式導(dǎo)致的代碼混亂外,還有就是對(duì)于 API 組合本身可能存在并行或先后調(diào)用,對(duì)于采用回調(diào)方式往往很難控制。

2、服務(wù)網(wǎng)關(guān)的基本功能:

a8d5877a-427b-11ed-96c9-dac502259ad0.png

3、流量網(wǎng)關(guān)與服務(wù)網(wǎng)關(guān)的區(qū)別:

a91cdb98-427b-11ed-96c9-dac502259ad0.png

流量網(wǎng)關(guān)和服務(wù)網(wǎng)關(guān)在系統(tǒng)整體架構(gòu)中所處的位置如上圖所示,流量網(wǎng)關(guān)(如Nignx)是指提供全局性的、與后端業(yè)務(wù)應(yīng)用無關(guān)的策略,例如 HTTPS證書卸載、Web防火墻、全局流量監(jiān)控等。

而微服務(wù)網(wǎng)關(guān)(如Spring Cloud Gateway)是指與業(yè)務(wù)緊耦合的、提供單個(gè)業(yè)務(wù)域級(jí)別的策略,如服務(wù)治理、身份認(rèn)證等。也就是說,流量網(wǎng)關(guān)負(fù)責(zé)南北向流量調(diào)度及安全防護(hù),微服務(wù)網(wǎng)關(guān)負(fù)責(zé)東西向流量調(diào)度及服務(wù)治理。

二、服務(wù)網(wǎng)關(guān)的部署:

1、主流網(wǎng)關(guān)的對(duì)比與選型:

a945aba4-427b-11ed-96c9-dac502259ad0.png

Kong 網(wǎng)關(guān) :Kong 的性能非常好,非常適合做流量網(wǎng)關(guān),但是對(duì)于復(fù)雜系統(tǒng)不建議業(yè)務(wù)網(wǎng)關(guān)用 Kong,主要是工程性方面的考慮

Zuul1.x 網(wǎng)關(guān) :Zuul 1.0 的落地經(jīng)驗(yàn)豐富,但是性能差、基于同步阻塞IO,適合中小架構(gòu),不適合并發(fā)流量高的場景,因?yàn)槿菀桩a(chǎn)生線程耗盡,導(dǎo)致請(qǐng)求被拒絕的情況

gateway 網(wǎng)關(guān) :功能強(qiáng)大豐富,性能好,官方基準(zhǔn)測試 RPS (每秒請(qǐng)求數(shù))是Zuul的1.6倍,能與 SpringCloud 生態(tài)很好兼容,單從流式編程+支持異步上也足以讓開發(fā)者選擇它了。

Zuul 2.x :性能與 gateway 差不多,基于非阻塞的,支持長連接,但 SpringCloud 沒有集成 zuul2 的計(jì)劃,并且 Netflix 相關(guān)組件都宣布進(jìn)入維護(hù)期,前景未知。

綜上,gateway 網(wǎng)關(guān)更加適合 SpringCloud 項(xiàng)目,而從發(fā)展趨勢上看,gateway 替代 zuul 也是必然的。

2、Spring Cloud Gateway 網(wǎng)關(guān)的搭建:

(1)聲明依賴版本號(hào):


2.3.2.RELEASE
Hoxton.SR9
2.2.6.RELEASE


 


 

org.springframework.boot
spring-boot-dependencies
${spring-boot.version}
pom
import

 

org.springframework.cloud
spring-cloud-dependencies
${spring-cloud.version}
pom
import

 

com.alibaba.cloud
spring-cloud-alibaba-dependencies
${spring-cloud-alibaba.version}
pom
import



(2)添加依賴:

 

org.springframework.cloud
spring-cloud-starter-gateway


org.springframework.boot
spring-boot-starter-web



注意:一定要排除掉 spring-boot-starter-web 依賴,否則啟動(dòng)報(bào)錯(cuò)

(3)配置項(xiàng)目名與端口

server:
port:9023
servlet:
context-path:/${spring.application.name}
spring:
application:
name:gateway

好了,網(wǎng)關(guān)項(xiàng)目搭建完成,其實(shí)就添加這么一個(gè)依賴,關(guān)于詳細(xì)的配置以及作用下文介紹。

3、Spring Cloud Gateway 配置項(xiàng)的說明:

在介紹 Spring Cloud Gateway 的配置項(xiàng)之前,我們先了解幾個(gè) Spring Cloud Gateway 的核心術(shù)語:

斷言(Predicate) :參照 Java8 的新特性Predicate,允許開發(fā)人員匹配 HTTP 請(qǐng)求中的任何內(nèi)容,比如請(qǐng)求頭或請(qǐng)求參數(shù),最后根據(jù)匹配結(jié)果返回一個(gè)布爾值。

路由(route) :由ID、目標(biāo)URI、斷言集合和過濾器集合組成。如果聚合斷言結(jié)果為真,則轉(zhuǎn)發(fā)到該路由。

過濾器(filter) :可以在返回請(qǐng)求之前或之后修改請(qǐng)求和響應(yīng)的內(nèi)容。

3.1、路由 Route:

Route 主要由 路由id、目標(biāo)uri、斷言集合和過濾器集合組成,那我們簡單看看這些屬性到底有什么作用。

id :路由標(biāo)識(shí),要求唯一,名稱任意(默認(rèn)值 uuid,一般不用,需要自定義)

uri :請(qǐng)求最終被轉(zhuǎn)發(fā)到的目標(biāo)地址

order :路由優(yōu)先級(jí),數(shù)字越小,優(yōu)先級(jí)越高

predicates :斷言數(shù)組,即判斷條件,如果返回值是boolean,則轉(zhuǎn)發(fā)請(qǐng)求到 uri 屬性指定的服務(wù)中

filters :過濾器數(shù)組,在請(qǐng)求傳遞過程中,對(duì)請(qǐng)求做一些修改

3.2、斷言 Predicate:

Predicate 來自于 Java8 的接口。Predicate 接受一個(gè)輸入?yún)?shù),返回一個(gè)布爾值結(jié)果。該接口包含多種默認(rèn)方法來將 Predicate 組合成其他復(fù)雜的邏輯(比如:與,或,非)。

Predicate 可以用于接口請(qǐng)求參數(shù)校驗(yàn)、判斷新老數(shù)據(jù)是否有變化需要進(jìn)行更新操作。Spring Cloud Gateway 內(nèi)置了許多 Predict,這些 Predict 的源碼在 org.springframework.cloud.gateway.handler.predicate 包中,有興趣可以閱讀一下。內(nèi)置的一些斷言如下圖:

a95b130e-427b-11ed-96c9-dac502259ad0.png以上11種斷言這里就不再介紹如何配置了,官方文檔寫的很清楚:

https://docs.spring.io/spring-cloud-gateway/docs/2.2.9.RELEASE/reference/html/

下面就以最后一種權(quán)重?cái)嘌詾槔榻B一下如何配置。配置如下:

spring:
cloud:
gateway:
#路由數(shù)組:指當(dāng)請(qǐng)求滿足什么樣的斷言時(shí),轉(zhuǎn)發(fā)到哪個(gè)服務(wù)上
routes:
#路由標(biāo)識(shí),要求唯一,名稱任意
-id:gateway-provider_1
#請(qǐng)求最終被轉(zhuǎn)發(fā)到的目標(biāo)地址
uri:http://localhost:9024
#設(shè)置斷言
predicates:
#PathRoutePredicateFactory斷言,滿足/gateway/provider/**路徑的請(qǐng)求都會(huì)被路由到http://localhost:9024這個(gè)uri中
-Path=/gateway/provider/**
#WeightRoutePredicateFactory斷言,同一分組按照權(quán)重進(jìn)行分配流量,這里分配了80%
#第一個(gè)group1是分組名,第二個(gè)參數(shù)是權(quán)重
-Weight=group1,8
#配置過濾器(局部)
filters:
#StripPrefix:去除原始請(qǐng)求路徑中的前1級(jí)路徑,即/gateway
-StripPrefix=1

-id:gateway-provider_2
uri:http://localhost:9025
#設(shè)置斷言
predicates:
-Path=/gateway/provider/**
#WeightRoutePredicateFactory,同一分組按照權(quán)重進(jìn)行分配流量,這里分配了20%
-Weight=group1,2
#配置過濾器(局部)
filters:
#StripPrefix:去除原始請(qǐng)求路徑中的前1級(jí)路徑,即/gateway
-StripPrefix=1

Spring Cloud Gateway 中的斷言命名都是有規(guī)范的,格式:“xxx + RoutePredicateFactory”,比如權(quán)重?cái)嘌?WeightRoutePredicateFactory,那么配置時(shí)直接取前面的 “Weight”。

如果路由轉(zhuǎn)發(fā)匹配到了兩個(gè)或以上,則是的按照配置先后順序轉(zhuǎn)發(fā),上面都配置了路徑:“ Path=/gateway/provider/** ”,如果沒有配置權(quán)重,則肯定是先轉(zhuǎn)發(fā)到 “http://localhost:9024”,但是既然配置配置了權(quán)重并且相同的分組,則按照權(quán)重比例進(jìn)行分配流量。

3.3、過濾器 filter:

Gateway 過濾器的生命周期:

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ā)送給客戶端等。

Gateway 過濾器從作用范圍可分為兩種:

GatewayFilter :應(yīng)用到單個(gè)路由或者一個(gè)分組的路由上(需要在配置文件中配置)

GlobalFilter :應(yīng)用到所有的路由上(無需配置,全局生效)

(1)局部過濾器 GatewayFilter:

Spring Cloud Gateway 中內(nèi)置了許多的局部過濾器,如下圖:

a996e37a-427b-11ed-96c9-dac502259ad0.png

局部過濾器需要在指定路由配置才能生效,默認(rèn)是不生效的。以 “AddResponseHeaderGatewayFilterFactory” 這個(gè)過濾器為例,為原始響應(yīng)添加Header,配置如下:

spring:
cloud:
gateway:
routes:
-id:gateway-provider_1
uri:http://localhost:9024
predicates:
-Path=/gateway/provider/**
#配置過濾器(局部)
filters:
-AddResponseHeader=X-Response-Foo,Bar
#StripPrefix:去除原始請(qǐng)求路徑中的前1級(jí)路徑,即/gateway
-StripPrefix=1

瀏覽器請(qǐng)求,發(fā)現(xiàn)響應(yīng)頭中已經(jīng)有了 X-Response-Foo=Bar 這個(gè)鍵值對(duì),如下圖:

a9a867e4-427b-11ed-96c9-dac502259ad0.png

在前面的示例中,我們也使用到了另一個(gè)局部過濾器 StripPrefixGatewayFilterFactory,該過濾器主要用于截?cái)嘣颊?qǐng)求的路徑,當(dāng)我們請(qǐng)求 localhost:9023/gateway/provider/test 時(shí),實(shí)際請(qǐng)求會(huì)被轉(zhuǎn)發(fā)到 http://localhost:9024 服務(wù)上,并被截?cái)喑?“http://localhost:9024/provider/test"

注意:過濾器的名稱只需要寫前綴,過濾器命名必須是 "xxx + GatewayFilterFactory“(包括自定義)。

更多過濾器的配置參考官方文檔:

https://docs.spring.io/spring-cloud-gateway/docs/2.2.9.RELEASE/reference/html/#gatewayfilter-factories

(2)自定義局部過濾器:

雖說內(nèi)置的過濾器能夠解決很多場景,但是難免還是有些特殊需求需要定制一個(gè)過濾器,下面就來介紹一下如何自定義局部過濾器。

/**
*名稱必須是xxxGatewayFilterFactory形式
*todo:模擬授權(quán)的驗(yàn)證,具體邏輯根據(jù)業(yè)務(wù)完善
*/
@Component
@Slf4j
publicclassAuthorizeGatewayFilterFactoryextendsAbstractGatewayFilterFactory{

privatestaticfinalStringAUTHORIZE_TOKEN="token";

//構(gòu)造函數(shù),加載Config
publicAuthorizeGatewayFilterFactory(){
//固定寫法
super(AuthorizeGatewayFilterFactory.Config.class);
log.info("LoadedGatewayFilterFactory[Authorize]");
}

//讀取配置文件中的參數(shù)賦值到配置類中
@Override
publicListshortcutFieldOrder(){
//Config.enabled
returnArrays.asList("enabled");
}

@Override
publicGatewayFilterapply(AuthorizeGatewayFilterFactory.Configconfig){
return(exchange,chain)->{
//判斷是否開啟授權(quán)驗(yàn)證
if(!config.isEnabled()){
returnchain.filter(exchange);
}

ServerHttpRequestrequest=exchange.getRequest();
HttpHeadersheaders=request.getHeaders();
//從請(qǐng)求頭中獲取token
Stringtoken=headers.getFirst(AUTHORIZE_TOKEN);
if(token==null){
//從請(qǐng)求頭參數(shù)中獲取token
token=request.getQueryParams().getFirst(AUTHORIZE_TOKEN);
}

ServerHttpResponseresponse=exchange.getResponse();
//如果token為空,直接返回401,未授權(quán)
if(StringUtils.isEmpty(token)){
response.setStatusCode(HttpStatus.UNAUTHORIZED);
//處理完成,直接攔截,不再進(jìn)行下去
returnresponse.setComplete();
}
/**
*todochain.filter(exchange)之前的都是過濾器的前置處理
*
*chain.filter().then(
*過濾器的后置處理...........
*)
*/
//授權(quán)正常,繼續(xù)下一個(gè)過濾器鏈的調(diào)用
returnchain.filter(exchange);
};
}

@Data
@AllArgsConstructor
@NoArgsConstructor
publicstaticclassConfig{
//控制是否開啟認(rèn)證
privatebooleanenabled;
}
}

局部過濾器需要在路由中配置才能生效,配置如下:

spring:
cloud:
gateway:
routes:
-id:gateway-provider_1
uri:http://localhost:9024
predicates:
-Path=/gateway/provider/**
#配置過濾器(局部)
filters:
-AddResponseHeader=X-Response-Foo,Bar
#AuthorizeGatewayFilterFactory自定義過濾器配置,值為true需要驗(yàn)證授權(quán),false不需要
-Authorize=true

此時(shí)直接訪問:http://localhost:9023/gateway/provider/port,不攜帶token,返回如下圖:

a9b968b4-427b-11ed-96c9-dac502259ad0.png

請(qǐng)求參數(shù)帶上token:http://localhost:9023/gateway/provider/port?token=abcdcdecd-ddcdeicd12,成功返回,如下圖:

a9d32cf4-427b-11ed-96c9-dac502259ad0.png

上述的 AuthorizeGatewayFilterFactory 只是涉及到了過濾器的前置處理,后置處理是在 chain.filter().then() 中的 then() 方法中完成的,具體可以看下項(xiàng)目源碼中的 TimeGatewayFilterFactory,代碼就不再貼出來了,如下圖:

a9e2a0e4-427b-11ed-96c9-dac502259ad0.png

(3)GlobalFilter 全局過濾器:

全局過濾器應(yīng)用全部路由上,無需開發(fā)者配置,Spring Cloud Gateway 也內(nèi)置了一些全局過濾器,如下圖:

aa0fca9c-427b-11ed-96c9-dac502259ad0.png

GlobalFilter 的功能其實(shí)和 GatewayFilter 是相同的,只是 GlobalFilter 的作用域是所有的路由配置,而不是綁定在指定的路由配置上。多個(gè) GlobalFilter 可以通過 @Order 或者 getOrder() 方法指定執(zhí)行順序,order值越小,執(zhí)行的優(yōu)先級(jí)越高。

注意,由于過濾器有 pre 和 post 兩種類型,pre 類型過濾器如果 order 值越小,那么它就應(yīng)該在pre過濾器鏈的頂層,post 類型過濾器如果 order 值越小,那么它就應(yīng)該在 post 過濾器鏈的底層。示意圖如下:

aa3c75a6-427b-11ed-96c9-dac502259ad0.png

(4)自定義全局過濾器:

當(dāng)然除了內(nèi)置的全局過濾器,實(shí)際工作中還需要定制過濾器,下面來介紹一下如何自定義。我們模擬 Nginx 的 Access Log 功能,記錄每次請(qǐng)求的相關(guān)信息。代碼如下:

@Slf4j
@Component
@Order(value=Integer.MIN_VALUE)
publicclassAccessLogGlobalFilterimplementsGlobalFilter{

@Override
publicMonofilter(ServerWebExchangeexchange,GatewayFilterChainchain){
//filter的前置處理
ServerHttpRequestrequest=exchange.getRequest();
Stringpath=request.getPath().pathWithinApplication().value();
InetSocketAddressremoteAddress=request.getRemoteAddress();
returnchain
//繼續(xù)調(diào)用filter
.filter(exchange)
//filter的后置處理
.then(Mono.fromRunnable(()->{
ServerHttpResponseresponse=exchange.getResponse();
HttpStatusstatusCode=response.getStatusCode();
log.info("請(qǐng)求路徑:{},遠(yuǎn)程IP地址:{},響應(yīng)碼:{}",path,remoteAddress,statusCode);
}));
}
}

好了,全局過濾器不必在路由上配置,注入到IOC容器中即可全局生效。

此時(shí)發(fā)出一個(gè)請(qǐng)求,控制臺(tái)打印信息如下:

請(qǐng)求路徑:/gateway/provider/port,遠(yuǎn)程IP地址:/000064114,響應(yīng)碼:200 OK

4、Gateway 集成 nacos 注冊中心實(shí)現(xiàn)服務(wù)發(fā)現(xiàn):

上述 demo 中并沒有集成注冊中心,每次路由配置都是指定固定的服務(wù)uri,如下圖:

aa9f0afe-427b-11ed-96c9-dac502259ad0.png

這樣做有什么壞處呢?

網(wǎng)關(guān)服務(wù)需要知道所有服務(wù)的域名或IP地址,另外,一旦服務(wù)的域名或IP地址發(fā)生修改,路由配置中的 uri 就必須修改

服務(wù)集群中無法實(shí)現(xiàn)負(fù)載均衡

那么此時(shí)我們可以集成的注冊中心,使得網(wǎng)關(guān)能夠從注冊中心自動(dòng)獲取uri,并實(shí)現(xiàn)負(fù)載均衡,這里我們以 nacos 注冊中心為例介紹一下

(1)pom 文件中新增依賴:

 

com.alibaba.cloud
spring-cloud-starter-alibaba-nacos-discovery

(2)啟動(dòng)類添加 @EnableDiscoveryClient 注解開啟注冊中心功能,如下圖:

aaca985e-427b-11ed-96c9-dac502259ad0.png

(3)配置 nacos 注冊中心的地址:

nacos:
namespace:856a40d7-6548-4494-bdb9-c44491865f63
url:120.76.129.106:80
spring:
cloud:
nacos:
discovery:
server-addr:${nacos.url}
namespace:${nacos.namespace}
register-enabled:true

(4)服務(wù)路由配置:

spring:
cloud:
gateway:
routes:
-id:gateway-provider_1
#使用了lb形式,從注冊中心負(fù)載均衡的獲取uri
uri:lb://gateway-provider
#配置斷言
predicates:
-Path=/gateway/provider/**
filters:
-AddResponseHeader=X-Response-Foo,Bar

路由配置中唯一不同的就是路由的 uri,格式:lb://service-name,這是固定寫法:

lb:固定格式,指的是從nacos中按照名稱獲取微服務(wù),并遵循負(fù)載均衡策略

service-name:nacos注冊中心的服務(wù)名稱,這里并不是IP地址形式的

為什么指定了 lb 就可以開啟負(fù)載均衡,前面說過全局過濾器 LoadBalancerClientFilter 就是負(fù)責(zé)路由尋址和負(fù)載均衡的,可以看到如下源碼:

aae7977e-427b-11ed-96c9-dac502259ad0.png

(5)開啟 gateway 自動(dòng)路由配置:

隨著我們的系統(tǒng)架構(gòu)不斷地發(fā)展,系統(tǒng)中微服務(wù)的數(shù)量肯定會(huì)越來越多,我們不可能每添加一個(gè)服務(wù),就在網(wǎng)關(guān)配置一個(gè)新的路由規(guī)則,這樣的維護(hù)成本很大;特別在很多種情況,我們在請(qǐng)求路徑中會(huì)攜帶一個(gè)路由標(biāo)識(shí)方便進(jìn)行轉(zhuǎn)發(fā),而這個(gè)路由標(biāo)識(shí)一般都是服務(wù)在注冊中心中的服務(wù)名,因此這是我們就可以開啟 spring cloud gateway 的自動(dòng)路由功能,網(wǎng)關(guān)自動(dòng)根據(jù)注冊中心的服務(wù)名為每個(gè)服務(wù)創(chuàng)建一個(gè)router,將以服務(wù)名開頭的請(qǐng)求路徑轉(zhuǎn)發(fā)到對(duì)應(yīng)的服務(wù),配置如下:

>基于SpringCloudAlibaba+Gateway+Nacos+RocketMQ+Vue&Element實(shí)現(xiàn)的后臺(tái)管理系統(tǒng)+用戶小程序,支持RBAC動(dòng)態(tài)權(quán)限、多租戶、數(shù)據(jù)權(quán)限、工作流、三方登錄、支付、短信、商城等功能
>
>*項(xiàng)目地址:
>*視頻教程

#enabled:默認(rèn)為false,設(shè)置為true表明springcloudgateway開啟服務(wù)發(fā)現(xiàn)和路由的功能,網(wǎng)關(guān)自動(dòng)根據(jù)注冊中心的服務(wù)名為每個(gè)服務(wù)創(chuàng)建一個(gè)router,將以服務(wù)名開頭的請(qǐng)求路徑轉(zhuǎn)發(fā)到對(duì)應(yīng)的服務(wù)
spring.cloud.gateway.discovery.locator.enabled=true
#lowerCaseServiceId:啟動(dòng)locator.enabled=true自動(dòng)路由時(shí),路由的路徑默認(rèn)會(huì)使用大寫ID,若想要使用小寫ID,可將lowerCaseServiceId設(shè)置為true
spring.cloud.gateway.discovery.locator.lower-case-service-id=true

這里需要注意的是,由于我們的網(wǎng)關(guān)項(xiàng)目配置了 server.servlet.context-path 屬性,這會(huì)導(dǎo)致自動(dòng)路由失敗的問題,因此我們需要做如下兩個(gè)修改:

#重寫過濾鏈,解決項(xiàng)目設(shè)置了server.servlet.context-path導(dǎo)致locator.enabled=true默認(rèn)路由策略404的問題
spring.cloud.gateway.discovery.locator.filters[0]=PreserveHostHeader
@Configuration
publicclassGatewayConfig
{
@Value("${server.servlet.context-path}")
privateStringprefix;

/**
*過濾server.servlet.context-path屬性配置的項(xiàng)目路徑,防止對(duì)后續(xù)路由策略產(chǎn)生影響,因?yàn)間ateway網(wǎng)關(guān)不支持servlet
*/
@Bean
@Order(-1)
publicWebFilterapiPrefixFilter()
{
return(exchange,chain)->
{
ServerHttpRequestrequest=exchange.getRequest();
Stringpath=request.getURI().getRawPath();

path=path.startsWith(prefix)?path.replaceFirst(prefix,""):path;
ServerHttpRequestnewRequest=request.mutate().path(path).build();

returnchain.filter(exchange.mutate().request(newRequest).build());
};
}
}

至此,我們就開啟了 spring cloud gateway 的自動(dòng)路由功能,網(wǎng)關(guān)自動(dòng)根據(jù)注冊中心的服務(wù)名為每個(gè)服務(wù)創(chuàng)建一個(gè)router,將以服務(wù)名開頭的請(qǐng)求路徑轉(zhuǎn)發(fā)到對(duì)應(yīng)的服務(wù)。

假設(shè)我們的服務(wù)提供者在 nacos 注冊中心的服務(wù)名為 “gateway-provider”,這時(shí)我們只需要訪問 “http://localhost:9023/gateway/gateway-provider/test”,就可以將請(qǐng)求成功轉(zhuǎn)發(fā)過去了

5、Gateway 整合 Apollo 實(shí)現(xiàn)動(dòng)態(tài)路由配置:

上述例子都是將網(wǎng)關(guān)的一系列配置寫到項(xiàng)目的配置文件中,一旦路由策略發(fā)生改變必須要重啟項(xiàng)目,這樣維護(hù)成本很高,特別是服務(wù)網(wǎng)關(guān)作為系統(tǒng)的中心點(diǎn),一旦重啟出現(xiàn)問題,影響面將是十分巨大的,因此,我們將網(wǎng)關(guān)的配置存放到配置中心中,這樣由配置中心統(tǒng)一管理,一旦路由發(fā)生改變,只需要在配置中心修改即可,降低風(fēng)險(xiǎn)且實(shí)時(shí)失效。本部分就以 Apollo 配置中心為例介紹下如下實(shí)現(xiàn)動(dòng)態(tài)路由配置:

(1)添加 apollo 配置中心依賴:

 

com.ctrip.framework.apollo
apollo-client
1.7.0

(2)添加 Apollo 路由更改監(jiān)聽刷新類:

importcom.ctrip.framework.apollo.enums.PropertyChangeType;
importcom.ctrip.framework.apollo.model.ConfigChange;
importcom.ctrip.framework.apollo.model.ConfigChangeEvent;
importcom.ctrip.framework.apollo.spring.annotation.ApolloConfigChangeListener;
importorg.slf4j.Logger;
importorg.slf4j.LoggerFactory;
importorg.springframework.beans.BeansException;
importorg.springframework.beans.factory.annotation.Autowired;
importorg.springframework.cloud.context.environment.EnvironmentChangeEvent;
importorg.springframework.cloud.gateway.config.GatewayProperties;
importorg.springframework.cloud.gateway.event.RefreshRoutesEvent;
importorg.springframework.cloud.gateway.route.RouteDefinitionWriter;
importorg.springframework.context.ApplicationContext;
importorg.springframework.context.ApplicationContextAware;
importorg.springframework.context.ApplicationEventPublisher;
importorg.springframework.context.ApplicationEventPublisherAware;
importorg.springframework.context.annotation.Configuration;

importjava.util.ArrayList;

/**
*Apollo路由更改監(jiān)聽刷新
*/
@Configuration
publicclassGatewayPropertRefresherimplementsApplicationContextAware,ApplicationEventPublisherAware
{
privatestaticfinalLoggerlogger=LoggerFactory.getLogger(GatewayPropertRefresher.class);

privatestaticfinalStringID_PATTERN="spring\.cloud\.gateway\.routes\[\d+\]\.id";

privatestaticfinalStringDEFAULT_FILTER_PATTERN="spring\.cloud\.gateway\.default-filters\[\d+\]\.name";


privateApplicationContextapplicationContext;

privateApplicationEventPublisherpublisher;

@Autowired
privateGatewayPropertiesgatewayProperties;

@Autowired
privateRouteDefinitionWriterrouteDefinitionWriter;


@Override
publicvoidsetApplicationContext(ApplicationContextapplicationContext)throwsBeansException{
this.applicationContext=applicationContext;
}

@Override
publicvoidsetApplicationEventPublisher(ApplicationEventPublisherapplicationEventPublisher){
this.publisher=applicationEventPublisher;
}


/**
*監(jiān)聽路由修改
*/
@ApolloConfigChangeListener(interestedKeyPrefixes="spring.cloud.gateway.")
publicvoidonChange(ConfigChangeEventchangeEvent)
{
refreshGatewayProperties(changeEvent);
}

/**
*刷新路由信息
*/
privatevoidrefreshGatewayProperties(ConfigChangeEventchangeEvent)
{
logger.info("gateway網(wǎng)關(guān)配置刷新開始!");

preDestroyGatewayProperties(changeEvent);
//更新配置
this.applicationContext.publishEvent(newEnvironmentChangeEvent(changeEvent.changedKeys()));
//更新路由
refreshGatewayRouteDefinition();

logger.info("gateway網(wǎng)關(guān)配置刷新完成!");
}

/***
*GatewayProperties沒有@PreDestroy和destroy方法
*org.springframework.cloud.context.properties.ConfigurationPropertiesRebinder#rebind(java.lang.String)中destroyBean時(shí)不會(huì)銷毀當(dāng)前對(duì)象
*如果把spring.cloud.gateway.前綴的配置項(xiàng)全部刪除(例如需要?jiǎng)討B(tài)刪除最后一個(gè)路由的場景),initializeBean時(shí)也無法創(chuàng)建新的bean,則return當(dāng)前bean
*若仍保留有spring.cloud.gateway.routes[n]或spring.cloud.gateway.default-filters[n]等配置,initializeBean時(shí)會(huì)注入新的屬性替換已有的bean
*這個(gè)方法提供了類似@PreDestroy的操作,根據(jù)配置文件的實(shí)際情況把org.springframework.cloud.gateway.config.GatewayProperties#routes
*和org.springframework.cloud.gateway.config.GatewayProperties#defaultFilters兩個(gè)集合清空
*/
privatesynchronizedvoidpreDestroyGatewayProperties(ConfigChangeEventchangeEvent)
{
logger.info("PreDestroyGatewayProperties操作開始!");

finalbooleanneedClearRoutes=this.checkNeedClear(changeEvent,ID_PATTERN,this.gatewayProperties.getRoutes().size());
if(needClearRoutes)
{
this.gatewayProperties.setRoutes(newArrayList());
}

finalbooleanneedClearDefaultFilters=this.checkNeedClear(changeEvent,DEFAULT_FILTER_PATTERN,this.gatewayProperties.getDefaultFilters().size());
if(needClearDefaultFilters)
{
this.gatewayProperties.setRoutes(newArrayList());
}

logger.info("PreDestroyGatewayProperties操作完成!");
}


privatevoidrefreshGatewayRouteDefinition()
{
logger.info("RefreshingGatewayRouteDefinition操作開始!");

this.publisher.publishEvent(newRefreshRoutesEvent(this));

logger.info("GatewayRouteDefinitionrefreshed操作完成!");
}

/***
*根據(jù)changeEvent和定義的pattern匹配key,如果所有對(duì)應(yīng)PropertyChangeType為DELETED則需要清空GatewayProperties里相關(guān)集合
*/
privatebooleancheckNeedClear(ConfigChangeEventchangeEvent,Stringpattern,intexistSize){

returnchangeEvent.changedKeys().stream().filter(key->key.matches(pattern)).filter(key->
{
ConfigChangechange=changeEvent.getChange(key);
returnPropertyChangeType.DELETED.equals(change.getChangeType());
}).count()==existSize;
}
}

(3)暴露endpoint端點(diǎn):

#暴露endpoint端點(diǎn),暴露路由信息,有獲取所有路由、刷新路由、查看單個(gè)路由、刪除路由等方法
management.endpoints.web.exposure.include=*
management.endpoint.health.show-details=always

至此,我們就完成了 Gateway 網(wǎng)關(guān)整合 Apollo 配置中心實(shí)現(xiàn)動(dòng)態(tài)路由配置,一旦路由發(fā)生改變,只需要在配置中心修改即可被監(jiān)聽到并實(shí)時(shí)失效

如果有整合 Nacos 或 MySQL 進(jìn)行動(dòng)態(tài)路由配置的讀者可以參考以下兩篇文章:

(1)整合 Nacos 進(jìn)行動(dòng)態(tài)路由配置:

https://www.cnblogs.com/jian0110/p/12862569.html

(2)整合 MySQL 進(jìn)行動(dòng)態(tài)路由配置:

https://blog.csdn.net/qq_42714869/article/details/92794911

6、自定義全局異常處理器:

通過前面的測試可以看到一個(gè)現(xiàn)象:一旦路由的微服務(wù)下線或者失聯(lián)了,Spring Cloud Gateway直接返回了一個(gè)錯(cuò)誤頁面,如下圖:

ab6a851c-427b-11ed-96c9-dac502259ad0.png顯然這種異常信息不友好,前后端分離架構(gòu)中必須定制返回的異常信息。傳統(tǒng)的Spring Boot 服務(wù)中都是使用 @ControllerAdvice 來包裝全局異常處理的,但是由于服務(wù)下線,請(qǐng)求并沒有到達(dá)。因此必須在網(wǎng)關(guān)中也要定制一層全局異常處理,這樣才能更加友好的和客戶端交互。

Spring Cloud Gateway提供了多種全局處理的方式,今天只介紹其中一種方式,實(shí)現(xiàn)還算比較優(yōu)雅:

直接創(chuàng)建一個(gè)類 GlobalErrorExceptionHandler,實(shí)現(xiàn) ErrorWebExceptionHandler,重寫其中的 handle 方法,代碼如下:

/**
*用于網(wǎng)關(guān)的全局異常處理
*@Order(-1):優(yōu)先級(jí)一定要比ResponseStatusExceptionHandler低
*/
@Slf4j
@Order(-1)
@Component
@RequiredArgsConstructor
publicclassGlobalErrorExceptionHandlerimplementsErrorWebExceptionHandler{

privatefinalObjectMapperobjectMapper;

@SuppressWarnings({"rawtypes","unchecked","NullableProblems"})
@Override
publicMonohandle(ServerWebExchangeexchange,Throwableex){
ServerHttpResponseresponse=exchange.getResponse();
if(response.isCommitted()){
returnMono.error(ex);
}

//JOSN格式返回
response.getHeaders().setContentType(MediaType.APPLICATION_JSON);
if(exinstanceofResponseStatusException){
response.setStatusCode(((ResponseStatusException)ex).getStatus());
}

returnresponse.writeWith(Mono.fromSupplier(()->{
DataBufferFactorybufferFactory=response.bufferFactory();
try{
//todo返回響應(yīng)結(jié)果,根據(jù)業(yè)務(wù)需求,自己定制
CommonResponseresultMsg=newCommonResponse("500",ex.getMessage(),null);
returnbufferFactory.wrap(objectMapper.writeValueAsBytes(resultMsg));
}
catch(JsonProcessingExceptione){
log.error("Errorwritingresponse",ex);
returnbufferFactory.wrap(newbyte[0]);
}
}));
}
}

好了,全局異常處理已經(jīng)定制完成了,在測試一下,此時(shí)正常返回JSON數(shù)據(jù)了(JSON的樣式根據(jù)架構(gòu)需要自己定制),如下圖:

abb46290-427b-11ed-96c9-dac502259ad0.png

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • 網(wǎng)關(guān)
    +關(guān)注

    關(guān)注

    9

    文章

    5039

    瀏覽量

    52245
  • 服務(wù)器
    +關(guān)注

    關(guān)注

    12

    文章

    9596

    瀏覽量

    86968
  • spring
    +關(guān)注

    關(guān)注

    0

    文章

    340

    瀏覽量

    14795
  • 過濾器
    +關(guān)注

    關(guān)注

    1

    文章

    436

    瀏覽量

    20079
  • Cloud
    +關(guān)注

    關(guān)注

    0

    文章

    72

    瀏覽量

    5515

原文標(biāo)題:Spring Cloud Gateway 服務(wù)網(wǎng)關(guān)的部署與使用詳細(xì)教程

文章出處:【微信號(hào):芋道源碼,微信公眾號(hào):芋道源碼】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。

收藏 人收藏

    評(píng)論

    相關(guān)推薦

    如何用ACM簡化你的Spring Cloud服務(wù)環(huán)境配置管理

    摘要: 本文我們就如何使用阿里云ACM這樣的配置管理產(chǎn)品在Spring Cloud中替代Spring Cloud Config幫助簡化環(huán)境配置管理做一個(gè)簡單的示例,幫助你理解基于ACM
    發(fā)表于 02-02 14:18

    EDAS再升級(jí)!全面支持Spring Cloud應(yīng)用

    ,使用 Spring Cloud 框架開發(fā)的應(yīng)用會(huì)遇到很多管理瓶頸。在云環(huán)境中,發(fā)布與管理會(huì)變得十分復(fù)雜。例如,本地開發(fā)完成的應(yīng)用,需要登錄到每一臺(tái)服務(wù)器進(jìn)行發(fā)布和部署。后續(xù)還會(huì)伴隨著
    發(fā)表于 02-02 15:20

    使用阿里云ACM簡化你的Spring Cloud服務(wù)環(huán)境配置管理

    摘要: 本文我們就如何使用阿里云ACM這樣的配置管理產(chǎn)品在Spring Cloud中替代Spring Cloud Config幫助簡化環(huán)境配置管理做一個(gè)簡單的示例,幫助你理解基于ACM
    發(fā)表于 07-04 17:16

    Dubbo Cloud Native 之路的實(shí)踐與思考

    Spring Boot Project 以及匯報(bào) Dubbo 與 Cloud Native 整合過程中的一些實(shí)踐與思考,如適配 Spring Cloud 、
    發(fā)表于 07-05 16:05

    Spring Cloud Config公共配置解決方案

    Spring Cloud Config 多服務(wù)公共配置
    發(fā)表于 08-30 09:05

    服務(wù)網(wǎng)關(guān)gateway的相關(guān)資料推薦

    目錄微服務(wù)網(wǎng)關(guān) gateway 概述[路由器網(wǎng)關(guān) Zuul 概述]嵌入式 Zuul 反向代理微服務(wù)網(wǎng)關(guān) gateway 概述1、想象一下一個(gè)
    發(fā)表于 12-23 08:19

    使用Spring Cloud與Docker實(shí)戰(zhàn)微服務(wù)

    使用Spring Cloud與Docker實(shí)戰(zhàn)微服務(wù)
    發(fā)表于 09-09 08:31 ?7次下載
    使用<b class='flag-5'>Spring</b> <b class='flag-5'>Cloud</b>與Docker實(shí)戰(zhàn)微<b class='flag-5'>服務(wù)</b>

    RabbitRpc基于spring cloud的微服務(wù)rpc調(diào)用

    ./oschina_soft/gitee-spring-cloud-rabbitrpc.zip
    發(fā)表于 06-14 09:51 ?1次下載
    RabbitRpc基于<b class='flag-5'>spring</b> <b class='flag-5'>cloud</b>的微<b class='flag-5'>服務(wù)</b>rpc調(diào)用

    Spring Cloud Tencent發(fā)布最新匹配版本!

    Cloud 2022。此篇文章詳細(xì)講述了 Spring Cloud Tencent 從 2021 版本升級(jí)到 2022 版本的改動(dòng)點(diǎn)。
    的頭像 發(fā)表于 12-09 15:34 ?1240次閱讀

    基于Traefik自研的微服務(wù)網(wǎng)關(guān)

    數(shù)據(jù)平面主要功能是接入用戶的HTTP請(qǐng)求和微服務(wù)被拆分后的聚合。使用微服務(wù)網(wǎng)關(guān)統(tǒng)一對(duì)外暴露后端服務(wù)的API和契約,路由和過濾功能正是網(wǎng)關(guān)的核心能力模塊。另外,微
    的頭像 發(fā)表于 04-16 11:08 ?2933次閱讀

    我們的微服務(wù)中為什么需要網(wǎng)關(guān)

    玩過微服務(wù)的小伙伴對(duì) Spring Cloud 中的的 Spring Cloud Gateway
    的頭像 發(fā)表于 05-04 17:38 ?1426次閱讀
    我們的微<b class='flag-5'>服務(wù)</b>中為什么需要<b class='flag-5'>網(wǎng)關(guān)</b>?

    Spring Cloud :打造可擴(kuò)展的微服務(wù)網(wǎng)關(guān)

    Spring Cloud Gateway是一個(gè)基于Spring Framework 5和Project Reactor的反應(yīng)式編程模型的微服務(wù)網(wǎng)關(guān)
    的頭像 發(fā)表于 10-22 10:03 ?633次閱讀
    <b class='flag-5'>Spring</b> <b class='flag-5'>Cloud</b> :打造可擴(kuò)展的微<b class='flag-5'>服務(wù)網(wǎng)關(guān)</b>

    springcloud的網(wǎng)關(guān)是什么

    Spring Cloud網(wǎng)關(guān)Spring Cloud Gateway)是一種基于
    的頭像 發(fā)表于 12-03 15:54 ?1038次閱讀

    dubbo和spring cloud區(qū)別

    Dubbo和Spring Cloud是兩個(gè)非常流行的微服務(wù)框架,各有自己的特點(diǎn)和優(yōu)勢。在本文中,我們將詳細(xì)介紹Dubbo和Spring
    的頭像 發(fā)表于 12-04 14:47 ?1889次閱讀

    Spring Cloud Gateway網(wǎng)關(guān)框架

    Spring Cloud Gateway網(wǎng)關(guān)框架 本軟件微服務(wù)架構(gòu)中采用Spring
    的頭像 發(fā)表于 08-22 09:58 ?638次閱讀
    <b class='flag-5'>Spring</b> <b class='flag-5'>Cloud</b> <b class='flag-5'>Gateway</b><b class='flag-5'>網(wǎng)關(guān)</b>框架