偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

16 圖 | 深入理解 Spring Cloud Gateway 的原理

開發(fā) 前端
在 PassJava 項(xiàng)目中,我用到了 Spring Cloud Gateway 作為 API 網(wǎng)關(guān),客戶端的所有的請(qǐng)求都是先經(jīng)過網(wǎng)關(guān),然后再轉(zhuǎn)發(fā)到會(huì)員微服務(wù)、題目微服務(wù)等。

你好,我是悟空。

本篇給大家?guī)淼氖俏⒎?wù)框架中非常重要的一個(gè)組件:API 網(wǎng)關(guān)。

圖片

前言

在 PassJava 項(xiàng)目中,我用到了 Spring Cloud Gateway 作為 API 網(wǎng)關(guān),客戶端的所有的請(qǐng)求都是先經(jīng)過網(wǎng)關(guān),然后再轉(zhuǎn)發(fā)到會(huì)員微服務(wù)、題目微服務(wù)等。

比如 API 網(wǎng)關(guān)和會(huì)員微服務(wù)對(duì)應(yīng)的訪問地址如下:

API 網(wǎng)關(guān)地址:http://localhost:8060

會(huì)員微服務(wù)地址:http://localhost:14000

客戶端請(qǐng)求都是訪問的 API 網(wǎng)關(guān),然后網(wǎng)關(guān)轉(zhuǎn)發(fā)到會(huì)員微服務(wù),客戶端無需知道會(huì)員微服務(wù)的地址。

本篇將會(huì)以 PassJava 作為案例進(jìn)行講解。

PassJava 開源地址:https://github.com/Jackson0714/PassJava-Platform

為什么需要 API 網(wǎng)關(guān)

在 SpringBoot 單體架構(gòu)中,一般只有一個(gè)后端服務(wù),如下圖所示:

圖片

單體架構(gòu)訪問示例圖

但是在 SpringCloud 微服務(wù)架構(gòu)中,往往有多個(gè)微服務(wù),這些微服務(wù)可能部署在不同的機(jī)器上,而且一個(gè)微服務(wù)可能會(huì)擴(kuò)容成多個(gè)相同的微服務(wù),組成微服務(wù)集群。

圖片

微服務(wù)架構(gòu)訪問示例圖

這種情況下,會(huì)存在如下問題:

  • 如果需要添加鑒權(quán)功能,則需要對(duì)每個(gè)微服務(wù)進(jìn)行改造。
  • 如果需要對(duì)流量進(jìn)行控制,則需要對(duì)每個(gè)微服務(wù)進(jìn)行改造。
  • 跨域問題,需要對(duì)每個(gè)微服務(wù)進(jìn)行改造。
  • 存在安全問題,每個(gè)微服務(wù)需要暴露自己的 Endpoint 給客戶端。Endpoint 就是服務(wù)的訪問地址 + 端口。比如下面的地址:
http://order.passjava.cn:8000
  • 灰度發(fā)布、動(dòng)態(tài)路由需要對(duì)每個(gè)微服務(wù)進(jìn)行改造。

這個(gè)問題的痛點(diǎn)是各個(gè)微服務(wù)都是一個(gè)入口,有沒有辦法統(tǒng)一入口呢?

解決這個(gè)問題的方式就是在客戶端和服務(wù)器之間加個(gè)中間商就好了呀,只有中間商一個(gè)入口,這個(gè)中間商就是網(wǎng)關(guān)。

還有一個(gè)細(xì)節(jié)問題:多個(gè)微服務(wù)之間是如何通信的?這就要用到遠(yuǎn)程調(diào)用組件了,比如 OpenFeign。但是服務(wù)之間的調(diào)用是需要知道對(duì)方的 Endpoint 的,如果一個(gè)服務(wù)有多個(gè)微服務(wù),就需要通過負(fù)載均衡組件進(jìn)行流量分發(fā)。那微服務(wù)之間不就暴露 Endpoint 了嗎?這個(gè)沒有問題,畢竟只是后端服務(wù)知道,外界是不知道的。

圖片

為了幫助大家更容易理解網(wǎng)關(guān)的作用,這里有個(gè)網(wǎng)關(guān)、客戶端、微服務(wù)的三方通話。

網(wǎng)關(guān)對(duì)話

網(wǎng)關(guān):客戶端你好,你現(xiàn)在可以只跟我通信了,我可以將你本來想發(fā)給微服務(wù)的流量進(jìn)行轉(zhuǎn)發(fā),微服務(wù)處理完之后,將結(jié)果返回給我,我再給你。

客戶端:你沒有賺差價(jià)吧?

API 網(wǎng)關(guān):我可能會(huì)加些請(qǐng)求頭、做下認(rèn)證、鑒權(quán)、限流等。

客戶端:微服務(wù)不是自己可以做嗎?

API 網(wǎng)關(guān):但是每個(gè)微服務(wù)都得自己加,這就很麻煩了,都交給我就好了。

微服務(wù):網(wǎng)關(guān)你好,你會(huì)為我保密我的地址對(duì)嗎?

API 網(wǎng)關(guān):當(dāng)然,我給客戶端看的是我自己的地址,客戶端不需要知道你的地址,只需要知道你的 API 是哪個(gè)就行,剩下的交給我來轉(zhuǎn)發(fā)給你。

API 網(wǎng)關(guān)選型對(duì)比

業(yè)界比較出名的網(wǎng)關(guān):Spring Cloud Gateway、Netflix Zuul、Nginx、Kong、Alibaba Tengine。

作為 Spring Cloud 全家桶中的一款組件,當(dāng)然選擇 Spring Cloud Gateway 了。

最開始 Spring Cloud 推薦的網(wǎng)關(guān)是 Netflix Zuul 1.x,但是停止維護(hù)了,后來又有 Zuul 2.0,但是因?yàn)殚_發(fā)延期比較嚴(yán)重,Spring Cloud 官方自己開發(fā)了 Spring Cloud Gateway 網(wǎng)關(guān)組件,用于代替 Zuul 網(wǎng)關(guān)。

所以本篇我們只會(huì)講解 Spring Cloud Gateway 網(wǎng)關(guān)組件。

Spring Cloud Gateway 的工作流程

Gateway 的工作流程如下圖所示:

圖片

① 路由判斷;客戶端的請(qǐng)求到達(dá)網(wǎng)關(guān)后,先經(jīng)過 Gateway Handler Mapping 處理,這里面會(huì)做斷言(Predicate)判斷,看下符合哪個(gè)路由規(guī)則,這個(gè)路由映射后端的某個(gè)服務(wù)。

② 請(qǐng)求過濾:然后請(qǐng)求到達(dá) Gateway Web Handler,這里面有很多過濾器,組成過濾器鏈(Filter Chain),這些過濾器可以對(duì)請(qǐng)求進(jìn)行攔截和修改,比如添加請(qǐng)求頭、參數(shù)校驗(yàn)等等,有點(diǎn)像凈化污水。然后將請(qǐng)求轉(zhuǎn)發(fā)到實(shí)際的后端服務(wù)。這些過濾器邏輯上可以稱作 Pre-Filters,Pre 可以理解為“在...之前”。

③ 服務(wù)處理:后端服務(wù)會(huì)對(duì)請(qǐng)求進(jìn)行處理。

④ 響應(yīng)過濾:后端處理完結(jié)果后,返回給 Gateway 的過濾器再次做處理,邏輯上可以稱作 Post-Filters,Post 可以理解為“在...之后”。

⑤ 響應(yīng)返回:響應(yīng)經(jīng)過過濾處理后,返回給客戶端。

小結(jié):客戶端的請(qǐng)求先通過匹配規(guī)則找到合適的路由,就能映射到具體的服務(wù)。然后請(qǐng)求經(jīng)過過濾器處理后轉(zhuǎn)發(fā)給具體的服務(wù),服務(wù)處理后,再次經(jīng)過過濾器處理,最后返回給客戶端。

Spring Cloud Gateway 的斷言

斷言(Predicate)這個(gè)詞聽起來極其深?yuàn)W,它是一種編程術(shù)語,我們生活中根本就不會(huì)用它。說白了它就是對(duì)一個(gè)表達(dá)式進(jìn)行 if 判斷,結(jié)果為真或假,如果為真則做這件事,否則做那件事。

在 Gateway 中,如果客戶端發(fā)送的請(qǐng)求滿足了斷言的條件,則映射到指定的路由器,就能轉(zhuǎn)發(fā)到指定的服務(wù)上進(jìn)行處理。

斷言配置的示例如下,配置了兩個(gè)路由規(guī)則,有一個(gè) predicates 斷言配置,當(dāng)請(qǐng)求 url 中包含 api/thirdparty,就匹配到了第一個(gè)路由 route_thirdparty。(代碼示例來自我的開源項(xiàng)目 PassJava)。

圖片

圖片斷言配置

接下來我們看下 Route 路由和 Predicate 斷言的對(duì)應(yīng)關(guān)系:

圖片

斷言和路由的對(duì)應(yīng)關(guān)系原理圖

  • 一對(duì)多:一個(gè)路由規(guī)則可以包含多個(gè)斷言。如上圖中路由 Route1 配置了三個(gè)斷言 Predicate。
  • 同時(shí)滿足:如果一個(gè)路由規(guī)則中有多個(gè)斷言,則需要同時(shí)滿足才能匹配。如上圖中路由 Route2 配置了兩個(gè)斷言,客戶端發(fā)送的請(qǐng)求必須同時(shí)滿足這兩個(gè)斷言,才能匹配路由 Route2。
  • 第一個(gè)匹配成功:如果一個(gè)請(qǐng)求可以匹配多個(gè)路由,則映射第一個(gè)匹配成功的路由。如上圖所示,客戶端發(fā)送的請(qǐng)求滿足 Route3 和 Route4 的斷言,但是 Route3 的配置在配置文件中靠前,所以只會(huì)匹配 Route3。

常見的 Predicate 斷言配置如下所示,假設(shè)匹配路由成功后,轉(zhuǎn)發(fā)到 http://localhost:9001

圖片

圖片常見的 Predicate 斷言配置

代碼演示

下面演示 Gateway 中通過斷言來匹配路由的例子。

  • 新建一個(gè) Maven 工程,引入 Gateway 依賴。
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
  • 新建 application.yml 文件,添加 Gateway 的路由規(guī)則。
spring:
cloud:
gateway:
routes:
- id: route_qq
uri: http://www.qq.com
predicates:
- Query=url,qq
- id: route_baidu
uri: http://www.baidu.com
predicates:
- Query=url,baidu
server:
port: 8060

第一條路由規(guī)則:斷言為 Query=url,qq,表示當(dāng)請(qǐng)求路徑中包含 url=qq,則跳轉(zhuǎn)到http://www.qq.com

第二條路由規(guī)則:當(dāng)請(qǐng)求路徑中包含 url=baidu,則跳轉(zhuǎn)到http://www.baidu.com

Spring Cloud Gateway 動(dòng)態(tài)路由

在微服務(wù)架構(gòu)中,我們不會(huì)直接通過 IP + 端口的方式訪問微服務(wù),而是通過服務(wù)名的方式來訪問。如下圖所示,微服務(wù)中加入了注冊(cè)中心,多個(gè)微服務(wù)將自己注冊(cè)到了注冊(cè)中心,這樣注冊(cè)中心就保存了服務(wù)名和 IP+端口的映射關(guān)系。

圖片

圖片微服務(wù)注冊(cè)到注冊(cè)中心

接下來我們來看下加入 Gateway 后,請(qǐng)求是如何進(jìn)行轉(zhuǎn)發(fā)的。

客戶端先將請(qǐng)求發(fā)送給 Nginx,然后轉(zhuǎn)發(fā)到網(wǎng)關(guān),網(wǎng)關(guān)經(jīng)過斷言匹配到一個(gè)路由后,將請(qǐng)求轉(zhuǎn)發(fā)給指定 uri,這個(gè) uri 可以配置成 微服務(wù)的名字,比如 passjava-member。

那么這個(gè)服務(wù)名具體要轉(zhuǎn)發(fā)到哪個(gè) IP 地址和端口上呢?這個(gè)就依賴注冊(cè)中心的注冊(cè)表了,Gateway 從注冊(cè)中心拉取注冊(cè)表,就能知道服務(wù)名對(duì)應(yīng)具體的 IP + 端口,如果一個(gè)服務(wù)部署了多臺(tái)機(jī)器,則還可以通過負(fù)載均衡進(jìn)行請(qǐng)求的轉(zhuǎn)發(fā)。原理如下圖所示:

圖片

網(wǎng)關(guān)+注冊(cè)中心

對(duì)應(yīng)的配置為 uri 字段如下所示:

圖片

圖片uri: lb://passjava-question,表示將請(qǐng)求轉(zhuǎn)發(fā)給 passjava-question 微服務(wù),且支持負(fù)載均衡。lb 是 loadbalance(負(fù)載均衡) 單詞的縮寫。

那什么叫動(dòng)態(tài)路由呢?

當(dāng) passjava-question 服務(wù)添加一個(gè)微服務(wù),或者 IP 地址更換了,Gateway 都是可以感知到的,但是配置是不需要更新的。這里的動(dòng)態(tài)指的是微服務(wù)的集群個(gè)數(shù)、IP 和端口是動(dòng)態(tài)可變的。

代碼示例

案例:調(diào)用 OSS 第三方服務(wù),上傳文件到 OSS。(基于 PassJava 項(xiàng)目)

前提:前端頁面配置的統(tǒng)一訪問路徑是網(wǎng)關(guān)的地址:http://localhost:8060/api/,OSS 服務(wù)對(duì)應(yīng)的地址是http://localhost:14000。

期望結(jié)果:將前端請(qǐng)求

http://localhost:8060/api/thirdparty/v1/admin/oss/getPolicy

轉(zhuǎn)發(fā)到 OSS 服務(wù)。

http://localhost:14000/thirdparty/v1/admin/oss/getPolicy

配置網(wǎng)關(guān):

spring:
cloud:
gateway:
routes:
- id: route_thirdparty # 第三方微服務(wù)路由規(guī)則
uri: lb://passjava-thirdparty # 負(fù)載均衡,將請(qǐng)求轉(zhuǎn)發(fā)到注冊(cè)中心注冊(cè)的 passjava-thirdparty 服務(wù)
predicates: # 斷言
- Path=/api/thirdparty/** # 如果前端請(qǐng)求路徑包含 api/thirdparty,則應(yīng)用這條路由規(guī)則
filters: #過濾器
- RewritePath=/api/(?<segment>.*),/$\{segment} # 將跳轉(zhuǎn)路徑中包含的api替換成空

測(cè)試上傳文件成功。

圖片

圖片

接下來我們看下 Gateway 非常重要且核心的功能:過濾器。

Spring Cloud Gateway 的過濾器

網(wǎng)關(guān),顧名思義,就是網(wǎng)絡(luò)中的一道關(guān)卡,可以統(tǒng)一對(duì)請(qǐng)求和響應(yīng)進(jìn)行一些操作。

過濾器 Filter 的分類

過濾器 Filter 按照請(qǐng)求和響應(yīng)可以分為兩種:Pre 類型和 Post 類型。

Pre 類型:在請(qǐng)求被轉(zhuǎn)發(fā)到微服務(wù)之前,對(duì)請(qǐng)求進(jìn)行攔截和修改,例如參數(shù)校驗(yàn)、權(quán)限校驗(yàn)、流量監(jiān)控、日志輸出以及協(xié)議轉(zhuǎn)換等操作。

Post 類型:微服務(wù)處理完請(qǐng)求后,返回響應(yīng)給網(wǎng)關(guān),網(wǎng)關(guān)可以再次進(jìn)行處理,例如修改響應(yīng)內(nèi)容或響應(yīng)頭、日志輸出、流量監(jiān)控等。

另外一種分類是按照過濾器 Filter 作用的范圍進(jìn)行劃分:

GlobalFilter:全局過濾器,應(yīng)用在所有路由上的過濾器。

局部過濾器

GatewayFilter:局部過濾器,應(yīng)用在單個(gè)路由或一組路由上的過濾器。標(biāo)紅色表示比較常用的過濾器。

整理了一份 27 種自帶的 GatwayFilter 過濾器。

圖片

具體怎么用呢,這里有個(gè)示例,如果 URL 匹配成功,則去掉 URL 中的 “api”。

filters: #過濾器
- RewritePath=/api/(?<segment>.*),/$\{segment} # 將跳轉(zhuǎn)路徑中包含的 “api” 替換成空

當(dāng)然我們也可以自定義過濾器,本篇不做展開。

全局過濾器

整理了一份全局過濾器的表格,具體用法可以參照官方文檔。

官方文檔:https://cloud.spring.io/spring-cloud-static/Greenwich.SR2/single/spring-cloud.html#_global_filters

圖片

全局過濾器最常見的用法是進(jìn)行負(fù)載均衡。配置如下所示:

spring:
cloud:
gateway:
routes:
- id: route_member # 第三方微服務(wù)路由規(guī)則
uri: lb://passjava-member # 負(fù)載均衡,將請(qǐng)求轉(zhuǎn)發(fā)到注冊(cè)中心注冊(cè)的 passjava-member 服務(wù)
predicates: # 斷言
- Path=/api/member/** # 如果前端請(qǐng)求路徑包含 api/member,則應(yīng)用這條路由規(guī)則
filters: #過濾器
- RewritePath=/api/(?<segment>.*),/$\{segment} # 將跳轉(zhuǎn)路徑中包含的api替換成空

這里有個(gè)關(guān)鍵字 lb,用到了全局過濾器 LoadBalancerClientFilter,當(dāng)匹配到這個(gè)路由后,會(huì)將請(qǐng)求轉(zhuǎn)發(fā)到 passjava-member 服務(wù),且支持負(fù)載均衡轉(zhuǎn)發(fā),也就是先將 passjava-member 解析成實(shí)際的微服務(wù)的 host 和 port,然后再轉(zhuǎn)發(fā)給實(shí)際的微服務(wù)。

實(shí)現(xiàn)簡單的 token 認(rèn)證

在用 Gateway 做登錄認(rèn)證的時(shí)候,通常需要我們自定義一個(gè)過濾器做登錄認(rèn)證。

比如客戶端登錄時(shí),將用戶名和密碼發(fā)送給網(wǎng)關(guān),網(wǎng)關(guān)轉(zhuǎn)發(fā)給認(rèn)證服務(wù)器后,如果賬號(hào)密碼正確,則拿到一個(gè) JWT token,然后客戶端再訪問應(yīng)用服務(wù)時(shí),先將請(qǐng)求發(fā)送給網(wǎng)關(guān),網(wǎng)關(guān)統(tǒng)一做 JWT 認(rèn)證,如果 JWT 符合條件,再將請(qǐng)求轉(zhuǎn)發(fā)給應(yīng)用服務(wù)。

原理如下圖所示,紅色框框的部分就是待會(huì)我要演示的部分。

圖片

案例演示

下面做一個(gè)簡單的認(rèn)證實(shí)例??蛻舳藬y帶 token 訪問 member 服務(wù),網(wǎng)關(guān)會(huì)先校驗(yàn) token 的合法性,驗(yàn)證規(guī)則如下:

當(dāng)請(qǐng)求的 header 中包含 token,且 token = admin,則認(rèn)證通過。

當(dāng)驗(yàn)證通過后,就會(huì)將請(qǐng)求轉(zhuǎn)發(fā)給 member 服務(wù)。

代碼示例

先定義一個(gè)全局過濾器,驗(yàn)證 token 的合法性。

@Component
public class GlobalLoginFilter implements GlobalFilter, Ordered {

@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest request= exchange.getRequest();
String token = request.getHeaders().getFirst("token");
if(!StringUtils.isEmpty(token)){
if("admin".equals(token)){
return chain.filter(exchange);
}
}
exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
return exchange.getResponse().setComplete();
}

@Override
public int getOrder() {
return 0;
}
}
  • 測(cè)試 token 不正確的場景。

先測(cè)試在 header 中添加 token=123,響應(yīng)結(jié)果為 401  Unauthorized,沒有權(quán)限。

圖片

  • 測(cè)試 token 正確的場景。

然后測(cè)試在 header 中添加 token=admin,正常返回響應(yīng)數(shù)據(jù)。


圖片


責(zé)任編輯:武曉燕 來源: 悟空聊架構(gòu)
相關(guān)推薦

2021-03-10 10:55:51

SpringJava代碼

2022-08-22 08:04:25

Spring事務(wù)Atomicity

2022-01-14 12:28:18

架構(gòu)OpenFeign遠(yuǎn)程

2020-08-10 18:03:54

Cache存儲(chǔ)器CPU

2024-04-15 00:00:00

技術(shù)Attention架構(gòu)

2024-03-12 00:00:00

Sora技術(shù)數(shù)據(jù)

2022-11-04 09:43:05

Java線程

2022-09-05 08:39:04

kubernetesk8s

2024-11-01 08:57:07

2023-06-07 15:34:21

架構(gòu)層次結(jié)構(gòu)

2020-03-18 13:40:03

Spring事數(shù)據(jù)庫代碼

2020-11-04 15:35:13

Golang內(nèi)存程序員

2020-03-17 08:36:22

數(shù)據(jù)庫存儲(chǔ)Mysql

2019-07-01 13:34:22

vue系統(tǒng)數(shù)據(jù)

2022-09-05 22:22:00

Stream操作對(duì)象

2023-10-13 13:30:00

MySQL鎖機(jī)制

2021-05-27 11:30:54

SynchronizeJava代碼

2020-03-26 16:40:07

MySQL索引數(shù)據(jù)庫

2023-09-19 22:47:39

Java內(nèi)存

2022-09-26 08:01:31

線程LIFO操作方式
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)