在线观看www成人影院-在线观看www日本免费网站-在线观看www视频-在线观看操-欧美18在线-欧美1级

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

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

Spring Cloud Gateway服務網關的部署與使用詳細教程

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

一、為什么需要服務網關:

1、什么是服務網關:

2、服務網關的基本功能:

3、流量網關與服務網關的區(qū)別:

二、服務網關的部署:

1、主流網關的對比與選型:

2、Spring Cloud Gateway 網關的搭建:

3、Spring Cloud Gateway 配置項的說明:

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

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

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

一、為什么需要服務網關:

1、什么是服務網關:

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

網關作為系統(tǒng)的唯一流量入口,封裝內部系統(tǒng)的架構,所有請求都先經過網關,由網關將請求路由到合適的微服務,所以,使用網關的好處在于:

簡化客戶端的工作。 網關將微服務封裝起來后,客戶端只需同網關交互,而不必調用各個不同服務;

降低函數間的耦合度。 一旦服務接口修改,只需修改網關的路由策略,不必修改每個調用該函數的客戶端,從而減少了程序間的耦合性

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

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

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

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

2、服務網關的基本功能:

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

3、流量網關與服務網關的區(qū)別:

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

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

而微服務網關(如Spring Cloud Gateway)是指與業(yè)務緊耦合的、提供單個業(yè)務域級別的策略,如服務治理、身份認證等。也就是說,流量網關負責南北向流量調度及安全防護,微服務網關負責東西向流量調度及服務治理。

二、服務網關的部署:

1、主流網關的對比與選型:

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

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

Zuul1.x 網關 :Zuul 1.0 的落地經驗豐富,但是性能差、基于同步阻塞IO,適合中小架構,不適合并發(fā)流量高的場景,因為容易產生線程耗盡,導致請求被拒絕的情況

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

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

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

2、Spring Cloud Gateway 網關的搭建:

(1)聲明依賴版本號:


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 依賴,否則啟動報錯

(3)配置項目名與端口

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

好了,網關項目搭建完成,其實就添加這么一個依賴,關于詳細的配置以及作用下文介紹。

3、Spring Cloud Gateway 配置項的說明:

在介紹 Spring Cloud Gateway 的配置項之前,我們先了解幾個 Spring Cloud Gateway 的核心術語:

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

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

過濾器(filter) :可以在返回請求之前或之后修改請求和響應的內容。

3.1、路由 Route:

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

id :路由標識,要求唯一,名稱任意(默認值 uuid,一般不用,需要自定義)

uri :請求最終被轉發(fā)到的目標地址

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

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

filters :過濾器數組,在請求傳遞過程中,對請求做一些修改

3.2、斷言 Predicate:

Predicate 來自于 Java8 的接口。Predicate 接受一個輸入參數,返回一個布爾值結果。該接口包含多種默認方法來將 Predicate 組合成其他復雜的邏輯(比如:與,或,非)。

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

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

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

下面就以最后一種權重斷言為例介紹一下如何配置。配置如下:

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

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

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

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

3.3、過濾器 filter:

Gateway 過濾器的生命周期:

PRE :這種過濾器在請求被路由之前調用。我們可利用這種過濾器實現(xiàn)身份驗證、在集群中選擇請求的微服務、記錄調試信息等。

POST :這種過濾器在路由到微服務以后執(zhí)行。這種過濾器可用來為響應添加標準的 HTTP Header、收集統(tǒng)計信息和指標、將響應從微服務發(fā)送給客戶端等。

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

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

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

(1)局部過濾器 GatewayFilter:

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

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

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

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

瀏覽器請求,發(fā)現(xiàn)響應頭中已經有了 X-Response-Foo=Bar 這個鍵值對,如下圖:

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

在前面的示例中,我們也使用到了另一個局部過濾器 StripPrefixGatewayFilterFactory,該過濾器主要用于截斷原始請求的路徑,當我們請求 localhost:9023/gateway/provider/test 時,實際請求會被轉發(fā)到 http://localhost:9024 服務上,并被截斷成 “http://localhost:9024/provider/test"

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

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

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

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

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

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

privatestaticfinalStringAUTHORIZE_TOKEN="token";

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

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

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

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

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

@Data
@AllArgsConstructor
@NoArgsConstructor
publicstaticclassConfig{
//控制是否開啟認證
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需要驗證授權,false不需要
-Authorize=true

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

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

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

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

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

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

(3)GlobalFilter 全局過濾器:

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

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

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

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

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

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

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

@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ù)調用filter
.filter(exchange)
//filter的后置處理
.then(Mono.fromRunnable(()->{
ServerHttpResponseresponse=exchange.getResponse();
HttpStatusstatusCode=response.getStatusCode();
log.info("請求路徑:{},遠程IP地址:{},響應碼:{}",path,remoteAddress,statusCode);
}));
}
}

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

此時發(fā)出一個請求,控制臺打印信息如下:

請求路徑:/gateway/provider/port,遠程IP地址:/000064114,響應碼:200 OK

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

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

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

這樣做有什么壞處呢?

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

服務集群中無法實現(xiàn)負載均衡

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

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

 

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

(2)啟動類添加 @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)服務路由配置:

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

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

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

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

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

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

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

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

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

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

這里需要注意的是,由于我們的網關項目配置了 server.servlet.context-path 屬性,這會導致自動路由失敗的問題,因此我們需要做如下兩個修改:

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

/**
*過濾server.servlet.context-path屬性配置的項目路徑,防止對后續(xù)路由策略產生影響,因為gateway網關不支持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 的自動路由功能,網關自動根據注冊中心的服務名為每個服務創(chuàng)建一個router,將以服務名開頭的請求路徑轉發(fā)到對應的服務。

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

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

上述例子都是將網關的一系列配置寫到項目的配置文件中,一旦路由策略發(fā)生改變必須要重啟項目,這樣維護成本很高,特別是服務網關作為系統(tǒng)的中心點,一旦重啟出現(xiàn)問題,影響面將是十分巨大的,因此,我們將網關的配置存放到配置中心中,這樣由配置中心統(tǒng)一管理,一旦路由發(fā)生改變,只需要在配置中心修改即可,降低風險且實時失效。本部分就以 Apollo 配置中心為例介紹下如下實現(xiàn)動態(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網關配置刷新開始!");

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

logger.info("gateway網關配置刷新完成!");
}

/***
*GatewayProperties沒有@PreDestroy和destroy方法
*org.springframework.cloud.context.properties.ConfigurationPropertiesRebinder#rebind(java.lang.String)中destroyBean時不會銷毀當前對象
*如果把spring.cloud.gateway.前綴的配置項全部刪除(例如需要動態(tài)刪除最后一個路由的場景),initializeBean時也無法創(chuàng)建新的bean,則return當前bean
*若仍保留有spring.cloud.gateway.routes[n]或spring.cloud.gateway.default-filters[n]等配置,initializeBean時會注入新的屬性替換已有的bean
*這個方法提供了類似@PreDestroy的操作,根據配置文件的實際情況把org.springframework.cloud.gateway.config.GatewayProperties#routes
*和org.springframework.cloud.gateway.config.GatewayProperties#defaultFilters兩個集合清空
*/
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操作完成!");
}

/***
*根據changeEvent和定義的pattern匹配key,如果所有對應PropertyChangeType為DELETED則需要清空GatewayProperties里相關集合
*/
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端點:

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

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

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

(1)整合 Nacos 進行動態(tài)路由配置:

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

(2)整合 MySQL 進行動態(tài)路由配置:

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

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

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

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

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

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

/**
*用于網關的全局異常處理
*@Order(-1):優(yōu)先級一定要比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è)務需求,自己定制
CommonResponseresultMsg=newCommonResponse("500",ex.getMessage(),null);
returnbufferFactory.wrap(objectMapper.writeValueAsBytes(resultMsg));
}
catch(JsonProcessingExceptione){
log.error("Errorwritingresponse",ex);
returnbufferFactory.wrap(newbyte[0]);
}
}));
}
}

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

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

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

    關注

    9

    文章

    4469

    瀏覽量

    51103
  • 服務器
    +關注

    關注

    12

    文章

    9160

    瀏覽量

    85415
  • spring
    +關注

    關注

    0

    文章

    340

    瀏覽量

    14343
  • 過濾器
    +關注

    關注

    1

    文章

    429

    瀏覽量

    19612
  • Cloud
    +關注

    關注

    0

    文章

    68

    瀏覽量

    5355

原文標題:Spring Cloud Gateway 服務網關的部署與使用詳細教程

文章出處:【微信號:芋道源碼,微信公眾號:芋道源碼】歡迎添加關注!文章轉載請注明出處。

收藏 人收藏

    評論

    相關推薦

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

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

    EDAS再升級!全面支持Spring Cloud應用

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

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

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

    Dubbo Cloud Native 之路的實踐與思考

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

    Spring Cloud Config公共配置解決方案

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

    服務網關gateway的相關資料推薦

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

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

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

    RabbitRpc基于spring cloud的微服務rpc調用

    ./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'>服務</b>rpc調用

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

    Cloud 2022。此篇文章詳細講述了 Spring Cloud Tencent 從 2021 版本升級到 2022 版本的改動點。
    的頭像 發(fā)表于 12-09 15:34 ?1099次閱讀

    基于Traefik自研的微服務網關

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

    我們的微服務中為什么需要網關

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

    Spring Cloud :打造可擴展的微服務網關

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

    springcloud的網關是什么

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

    dubbo和spring cloud區(qū)別

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

    Spring Cloud Gateway網關框架

    Spring Cloud Gateway網關框架 本軟件微服務架構中采用Spring
    的頭像 發(fā)表于 08-22 09:58 ?493次閱讀
    <b class='flag-5'>Spring</b> <b class='flag-5'>Cloud</b> <b class='flag-5'>Gateway</b><b class='flag-5'>網關</b>框架
    主站蜘蛛池模板: 亚洲成人在线网 | 夜色综合 | 免费三级毛片 | 人人插人人爽 | 国产午夜毛片一区二区三区 | 国产手机在线 | 欧美精品综合一区二区三区 | 成 黄 色 激 情视频网站 | 色综合色综合 | 嫩草影院永久入口在线观看 | 精品国产免费观看久久久 | 99热在线获取最新地址 | 日韩一级在线播放免费观看 | 成年网站在线观看 | 久久久久综合中文字幕 | 色综合五月婷婷 | 日日干夜夜操s8 | 三浦理惠子中文在 | 亚洲jizzjizz中国妇女 | 高清性色生活片欧美在线 | 亚洲 图片 小说 欧美 另类 | 婷婷免费高清视频在线观看 | www.av小视频| 免费在线观看视频 | 日本大黄在线观看 | 人人爱人人澡 | 日本一区二区三区四区视频 | 欧美色88| 天堂网在线资源 | 亚色在线观看 | 人人干人人爱 | 九九九精品 | 欧美乱妇15p | 人人弄| 亚洲免费成人网 | 性色欧美| 爱爱欧美 | 五月婷婷六月激情 | 国产精品久久久久久久久kt | 国产h在线 | 天堂最新版 |