微服務(wù)網(wǎng)關(guān)升級(jí):Spring Cloud Gateway 整合 Nacos 實(shí)現(xiàn)服務(wù)請(qǐng)求自動(dòng)轉(zhuǎn)發(fā)+負(fù)載均衡!
一、背景介紹
本文將繼續(xù)研究 Gateway 的更高級(jí)用法,比如整合服務(wù)注冊(cè)中心實(shí)現(xiàn)請(qǐng)求自動(dòng)路由轉(zhuǎn)發(fā)、整合服務(wù)配置中心實(shí)現(xiàn)路由規(guī)則動(dòng)態(tài)加載等。
下面我們一起來(lái)看看相關(guān)的實(shí)現(xiàn)思路。
二、整合注冊(cè)中心
在上篇文章中,我們介紹了在 Spring Cloud Gateway 中通過(guò)配置的方式就可實(shí)現(xiàn)將請(qǐng)求轉(zhuǎn)發(fā)到某個(gè)目標(biāo)服務(wù)上。而在微服務(wù)架構(gòu)中,服務(wù)中心往往注冊(cè)了很多服務(wù),如果每個(gè)服務(wù)都進(jìn)行單獨(dú)配置的話,那這份工作無(wú)疑既勞累又枯燥。
實(shí)際上,Spring Cloud Gateway 提供了一種默認(rèn)轉(zhuǎn)發(fā)的能力,只要將 Spring Cloud Gateway 注冊(cè)到服務(wù)中心,Spring Cloud Gateway 默認(rèn)就會(huì)自動(dòng)代理服務(wù)中心的所有服務(wù),并以服務(wù)名作為目標(biāo) URI 來(lái)自動(dòng)創(chuàng)建動(dòng)態(tài)路由。
整個(gè)服務(wù)體系的工作流程就會(huì)變成如下圖。
圖片
下面我們以 Nacos 作為服務(wù)注冊(cè)為例,通過(guò)具體的案例看看如何使用 Spring Cloud Gateway 來(lái)實(shí)現(xiàn)將服務(wù)請(qǐng)求進(jìn)行轉(zhuǎn)發(fā)的效果。
在構(gòu)建服務(wù)網(wǎng)關(guān)之前,需要先部署并啟動(dòng) Nacos,這一步比較簡(jiǎn)單,在此就不重復(fù)介紹了。如果還不會(huì)的小伙伴,可以參考之前寫(xiě)過(guò)的 Nacos 作為服務(wù)注冊(cè)中心的技術(shù)文章。
2.1、構(gòu)建服務(wù)網(wǎng)關(guān)
使用 Spring Cloud Gateway 來(lái)構(gòu)建服務(wù)網(wǎng)關(guān)也非常簡(jiǎn)單,之前我們已經(jīng)詳細(xì)介紹過(guò),將之前創(chuàng)建的gateway-server
復(fù)制一個(gè)新服務(wù)網(wǎng)關(guān)工程,命名為gateway-nacos
,并在pom.xml
中引入 Nacos 注冊(cè)中心依賴包,示例如下:
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<spring-boot.version>2.2.5.RELEASE</spring-boot.version>
<spring-cloud.version>Hoxton.SR3</spring-cloud.version>
<spring-cloud-alibaba.version>2.2.3.RELEASE</spring-cloud-alibaba.version>
</properties>
<dependencies>
<!-- 引入 Spring Cloud Gateway 網(wǎng)關(guān)組件 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<!-- 引入 Spring Cloud Alibaba Nacos 作為注冊(cè)中心 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<!-- 引入 springBoot 版本號(hào) -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring-boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!-- 引入 spring cloud 版本號(hào) -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!-- 引入 spring cloud alibaba 適配的版本號(hào) -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>${spring-cloud-alibaba.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
2.2、修改配置文件
修改application.yaml
配置文件,增加 Nacos 注冊(cè)中心相關(guān)的配置項(xiàng)。
完整配置如下:
server:
port: 8080
spring:
application:
name: gateway-nacos
cloud:
# Spring Cloud Gateway 配置項(xiàng),對(duì)應(yīng) GatewayProperties 類
gateway:
# 與 Spring Cloud 注冊(cè)中心的集成,對(duì)應(yīng) DiscoveryLocatorProperties 類
discovery:
locator:
enabled: true # 是否開(kāi)啟,默認(rèn)為 false 關(guān)閉
url-expression: "'lb://' + serviceId" # 路由的目標(biāo)地址的表達(dá)式,默認(rèn)為"'lb://' + serviceId"
nacos:
discovery:
server-addr: 127.0.0.1:8848 # Nacos 服務(wù)器地址
關(guān)鍵參數(shù)作用解讀:
nacos.discovery.server-addr
:顧名思義,使用 Nacos 作為 Spring Cloud 的服務(wù)注冊(cè)中心gateway.discovery.locator.enabled
:是否開(kāi)啟與 Spring Cloud 注冊(cè)中心的集成功能,默認(rèn)false
,這里需要開(kāi)啟gateway.discovery.locator.url-expression
:路由的目標(biāo)地址的 Spring EL 表達(dá)式,默認(rèn)為"'lb://' + serviceId"
可能大家對(duì)url-expression
這個(gè)配置項(xiàng)不太理解,我們來(lái)舉個(gè)例子。
假設(shè)注冊(cè)中心有user-service
和order-service
兩個(gè)服務(wù),url-expression
這個(gè)配置項(xiàng)最終效果和如下配置等價(jià):
spring:
cloud:
gateway:
routes:
- id: ReactiveCompositeDiscoveryClient_user-service
uri: lb://user-service
predicates:
- Path=/user-service/**
filters:
- RewritePath=/user-service/(?<remaining>.*), /${remaining}
- id: ReactiveCompositeDiscoveryClient_order-service
uri: lb://order-service
predicates:
- Path=/order-service/**
filters:
- RewritePath=/order-service/(?<remaining>.*), /${remaining}
其中uri: lb://user-service
表達(dá)式是user-service
服務(wù)實(shí)例地址的一種簡(jiǎn)寫(xiě),lb://
前綴表示將請(qǐng)求以負(fù)載均衡方式轉(zhuǎn)發(fā)到對(duì)應(yīng)的目標(biāo)服務(wù)實(shí)例上。
2.3、構(gòu)建業(yè)務(wù)微服務(wù)
為了方便測(cè)試服務(wù)的路由效果,我們還需要?jiǎng)?chuàng)建一個(gè) Spring Boot 服務(wù),并將服務(wù)注冊(cè)到 Nacos,實(shí)現(xiàn)方式也很簡(jiǎn)單,只需如下幾步即可完成。
首先,創(chuàng)建一個(gè) SpringBoot 工程,命名為user-service
,其pom.xml
與上文類似,修改dependencies
內(nèi)容,改成如下內(nèi)容即可。
<dependencies>
<!-- SpringBoot web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Nacos 服務(wù)發(fā)現(xiàn) -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
</dependencies>
然后,創(chuàng)建一個(gè)服務(wù)啟動(dòng)類并添加@EnableDiscoveryClient
,將當(dāng)前服務(wù)注冊(cè)到 Nacos。
@EnableDiscoveryClient
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class,args);
}
}
接著,創(chuàng)建一個(gè) web 接口,以便測(cè)試服務(wù)的轉(zhuǎn)發(fā)效果,示例如下:
@RestController
public class HelloController {
@GetMapping("/hello")
public String hello() {
return "hello,我是用戶服務(wù)";
}
}
最后,創(chuàng)建application.properties
配置文件中添加服務(wù)注冊(cè)中心地址,示例如下:
spring.application.name=user-service
server.port=9010
# 設(shè)置Nacos的服務(wù)地址,多個(gè)地址可使用【,】分隔
spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848
2.4、服務(wù)測(cè)試
將服務(wù)網(wǎng)關(guān)和user-service
都啟動(dòng)起來(lái),訪問(wèn) Nacos 服務(wù)控制臺(tái),可以看到服務(wù)的注冊(cè)信息。
圖片
Spring Cloud Gateway 注冊(cè)到服務(wù)中心之后,網(wǎng)關(guān)會(huì)自動(dòng)代理所有注冊(cè)中心的服務(wù),訪問(wèn)這些服務(wù)的方式為:
http://網(wǎng)關(guān)地址:端口/服務(wù)中心注冊(cè) serviceId/具體服務(wù)接口的url
比如,訪問(wèn)http://127.0.0.1:8080/user-service/hello
,它會(huì)自動(dòng)轉(zhuǎn)發(fā)到user-service
服務(wù)的/hello
接口上,返回結(jié)果如下圖。
圖片
當(dāng)一個(gè)服務(wù)在多個(gè)機(jī)器上部署時(shí),服務(wù)網(wǎng)關(guān)會(huì)依次輪流請(qǐng)求,實(shí)現(xiàn)負(fù)載均衡的效果。
三、整合配置中心
在上文中,Spring Cloud Gateway 整合服務(wù)注冊(cè)中心之后,會(huì)自動(dòng)代理所有注冊(cè)中心的服務(wù)。
但是很多時(shí)候,我們并不想通過(guò)網(wǎng)關(guān)把服務(wù)都暴露出去,每個(gè)服務(wù)的路由規(guī)則可能不同,會(huì)存在配置不同過(guò)濾器的情況,并且可能需要經(jīng)常經(jīng)常調(diào)整,這個(gè)時(shí)候如何處理呢?
此時(shí)可以借助服務(wù)配置中心,將路由規(guī)則從服務(wù)網(wǎng)關(guān)中抽離出來(lái),通過(guò)配置中心實(shí)現(xiàn)服務(wù)網(wǎng)關(guān)動(dòng)態(tài)加載路由規(guī)則。
Spring Cloud 支持的配置中心組件有很多,比如 Config、Apollo、Nacos 等。其中 Nacos 應(yīng)用比較廣泛,因?yàn)樗瓤梢宰龇?wù)注冊(cè)中心又可以做服務(wù)配置中心。
下面我們還是以 Nacos 作為服務(wù)配置中心為例,通過(guò)具體的案例看看如何使用 Spring Cloud Gateway 來(lái)實(shí)現(xiàn)將路由規(guī)則動(dòng)態(tài)加載效果。
3.1、構(gòu)建服務(wù)網(wǎng)關(guān)
還是以上文的gateway-nacos
服務(wù)網(wǎng)關(guān)工程為例,復(fù)制一個(gè)新的服務(wù)網(wǎng)關(guān)工程,命名為gateway-application
,并在pom.xml
中引入 Nacos 配置中心依賴包,示例如下:
<dependencies>
<!-- 引入 Spring Cloud Gateway 網(wǎng)關(guān)組件 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<!-- 引入 Spring Cloud Alibaba Nacos 作為注冊(cè)中心 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!-- 引入 Spring Cloud Alibaba Nacos 作為配置中心 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
</dependencies>
3.2、修改配置文件
因?yàn)?code style="background-color: rgb(231, 243, 237); padding: 1px 3px; border-radius: 4px; overflow-wrap: break-word; text-indent: 0px; display: inline-block;">Nacos配置中心的配置項(xiàng),只有在bootstrap.yaml
才能生效。
將application.yaml
文件修改成bootstrap.yaml
文件,并添加Nacos
配置中心相關(guān)的配置項(xiàng)。
配置內(nèi)容如下:
server:
port: 8080
spring:
application:
name: gateway-application
cloud:
nacos:
# Nacos 作為注冊(cè)中心
discovery:
server-addr: 127.0.0.1:8848
# Nacos 作為配置中心,對(duì)應(yīng) NacosConfigProperties 配置屬性類
config:
server-addr: 127.0.0.1:8848 # Nacos 服務(wù)器地址
namespace: # 對(duì)應(yīng) Nacos 的命名空間,默認(rèn)為 null
group: DEFAULT_GROUP # 對(duì)應(yīng) Nacos 配置分組,默認(rèn)為 DEFAULT_GROUP
name: gateway-config # 對(duì)應(yīng) Nacos 配置集的 dataId,默認(rèn)為 spring.application.name
file-extension: yaml
在上文中,我們配置了一個(gè)dataId
為gateway-config
,所屬分組為DEFAULT_GROUP
的配置文件。
接著,在 nacos 配置中心創(chuàng)建對(duì)應(yīng)的配置項(xiàng)并發(fā)布,示例如下:
圖片
配置內(nèi)容為:
spring:
cloud:
gateway:
routes:
- id: user-service
uri: lb://user-service
predicates:
- Path=/user-service/**
filters:
- RewritePath=/user-service/(?<remaining>.*), /${remaining}
當(dāng)網(wǎng)關(guān)啟動(dòng)時(shí)會(huì)自動(dòng)將 Nacos 中配置中心中的路由規(guī)則內(nèi)容載入到服務(wù)容器中,并自動(dòng)進(jìn)行刷新。
3.3、服務(wù)測(cè)試
最后將網(wǎng)關(guān)服務(wù)啟動(dòng),再次訪問(wèn)http://127.0.0.1:8080/user-service/hello
,會(huì)自動(dòng)轉(zhuǎn)發(fā)到user-service
服務(wù)的/hello
接口上。
圖片
回到 Nacos 配置中心頁(yè)面修改路由規(guī)則,將/user-service
路徑改成/user
。
圖片
然后訪問(wèn)http://127.0.0.1:8080/user/hello
,返回結(jié)果如下圖。
圖片
可以清晰的看到,服務(wù)網(wǎng)關(guān)中的路由規(guī)則也被動(dòng)態(tài)刷新了。
四、小結(jié)
最后總結(jié)一下,Spring Cloud Gateway 是一個(gè)功能非常強(qiáng)大的服務(wù)網(wǎng)關(guān),在微服務(wù)架構(gòu)中通常與服務(wù)注冊(cè)中心和配置中心搭配使用,以此完成服務(wù)接口的統(tǒng)一請(qǐng)求轉(zhuǎn)發(fā)效果。
五、參考
1.https://www.iocoder.cn/Spring-Cloud/Spring-Cloud-Gateway/?self