歡迎訪問我的GitHub
https://github.com/zq2599/blog_demos
內容:所有原創文章分類匯總及配套源碼,涉及Java、Docker、Kubernetes、DevOPS等;
本篇概覽
- 本文是《Spring Cloud Gateway實戰》系列的第二篇,通過前文咱們了解到Spring Cloud Gateway的核心是路由配置,然后在本地application.yml中配置了一條路由,但這種修改本地配置文件的方式缺乏靈活性,未必能滿足靈活多變的業務需求,因此,本篇的目的就是找出本地配置之外的其他配置方式來,滿足各種實際需求;
- 總的來說以下三種方式都是常用的:
- 目標地址支持用服務名(取代之前的IP+端口);
- 支持在nacos上配置;
- 支持寫代碼的方式配置;
- 另外還有一種更加靈活的配置方式:動態代理,因為涉及到不少的代碼所以會單獨出一篇文章詳細介紹
源碼下載
- 本篇實戰中的完整源碼可在GitHub下載到,地址和鏈接信息如下表所示(https://github.com/zq2599/blog_demos):
名稱 | 鏈接 | 備注 |
---|---|---|
項目主頁 | https://github.com/zq2599/blog_demos | 該項目在GitHub上的主頁 |
git倉庫地址(https) | https://github.com/zq2599/blog_demos.git | 該項目源碼的倉庫地址,https協議 |
git倉庫地址(ssh) | git@github.com:zq2599/blog_demos.git | 該項目源碼的倉庫地址,ssh協議 |
- 這個git項目中有多個文件夾,本篇的源碼在spring-cloud-tutorials文件夾下,如下圖紅框所示:
准備工作
-
正式開始前需要再做一點准備工作,整個《Spring Cloud Gateway實戰》系列中,所有請求最后都會被路由到provider-hello這個web上去,該服務目前只有一個web接口/hello/str,現在咱們再給它增加一個,后面的實戰會用到
-
新增加的web接口來自LBTest.java,可見非常簡單:
package com.bolingcavalry.provider.controller;
import com.bolingcavalry.common.Constants;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.text.SimpleDateFormat;
import java.util.Date;
@RestController
@RequestMapping("/lbtest")
public class LBTest {
private String dateStr(){
return new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").format(new Date());
}
/**
* 返回字符串類型
* @return
*/
@GetMapping("/str")
public String helloStr() {
return Constants.LB_PREFIX + ", " + dateStr();
}
}
- 上述代碼中的Constants.LB_PREFIX來自子工程common:
package com.bolingcavalry.common;
public interface Constants {
String HELLO_PREFIX = "Hello World";
String LB_PREFIX = "Load balance";
}
-
寫完代碼后,先確保nacos已經啟動
-
在啟動provider-hello工程,啟動成功后去看nacos,確認已經注冊:
- 准備完畢,可以開始實戰了
目標地址支持用服務名(取代之前的IP+端口)
- 咱們從最簡單的開始,先看前文的路由配置,如下圖紅框,目標地址是IP+端口:
-
玩過Spring Cloud的您自然看出了問題所在:沒有注冊發現,確實,這樣將地址和端口寫死在配置文件中是不合適的,咱們先來解決這個問題;
-
新增名為gateway-by-loadbalance的子工程,其pom.xml中的依賴情況如下,可見重點是spring-cloud-starter-loadbalancer:
<dependencies>
<dependency>
<groupId>com.bolingcavalry</groupId>
<artifactId>common</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- 路由策略使用lb的方式是,這個依賴一定要有 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
<!--nacos:注冊中心-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
</dependencies>
-
啟動類的代碼省去了(和前文的一樣)
-
配置信息如下,重點是uri的值lb://provider-hello,用了前綴lb:,后面的provider-hello就是在nacos注冊的服務名:
server:
#服務端口
port: 8085
spring:
application:
name: gateway-by-loadbalance
cloud:
nacos:
# 注冊中心的配置
discovery:
server-addr: 127.0.0.1:8848
gateway:
routes:
- id: path_route_lb
uri: lb://provider-hello
predicates:
- Path=/lbtest/**
- 單元測試類:
package com.bolingcavalry.gateway;
import com.bolingcavalry.common.Constants;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.reactive.AutoConfigureWebTestClient;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.MediaType;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import org.springframework.test.web.reactive.server.WebTestClient;
import static org.junit.jupiter.api.Assertions.assertTrue;
@SpringBootTest
@ExtendWith(SpringExtension.class)
@AutoConfigureWebTestClient
public class HelloTest {
@Autowired
private WebTestClient webClient;
@Test
void testLoadBalance() {
webClient.get()
.uri("/lbtest/str")
.accept(MediaType.APPLICATION_JSON)
.exchange()
// 驗證狀態
.expectStatus().isOk()
// 驗證結果,注意結果是字符串格式
.expectBody(String.class).consumeWith(result -> assertTrue(result.getResponseBody().contains(Constants.LB_PREFIX)));
}
}
- 運行單元測試,通過,可見上述配置可以通過前綴lb:准確找到服務:
支持在nacos上配置
-
將所有配置信息寫在application.yml中有個問題:不能遠程配置,這在應用數量較多的場景就不方便了,好在nacos提供了遠程配置的能力,應用啟動后可以從nacos取得自己的配置信息,咱們來試試
-
新增名為gateway-nacos-config的子工程,其pom.xml中的依賴情況如下,請注意里面的中文注釋,每指明了每一個依賴的作用:
<dependencies>
<dependency>
<groupId>com.bolingcavalry</groupId>
<artifactId>common</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- 使用bootstrap.yml的時候,這個依賴一定要有 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bootstrap</artifactId>
</dependency>
<!-- 路由策略使用lb的方式是,這個依賴一定要有 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
<!--nacos:配置中心-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
<!--nacos:注冊中心-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
</dependencies>
- 本地的配置文件bootstrap.yml,非常簡單,就是nacos的地址和遠程配置信息:
spring:
application:
name: gateway-nacos-config
cloud:
nacos:
config:
server-addr: 127.0.0.1:8848
file-extension: yml
group: DEFAULT_GROUP
- 接下來再nacos增加一個配置文件,操作如下圖紅框:
- 增加一個配置,要注意的地方如下(配置信息的文本稍后給出,便於復制):
- 上圖中完整的配置信息如下:
server:
port: 8083
spring:
cloud:
gateway:
routes:
- id: path_route_addr
uri: http://127.0.0.1:8082
predicates:
- Path=/hello/**
- id: path_route_lb
uri: lb://provider-hello
predicates:
- Path=/lbtest/**
- 測試類中的兩個測試方法如下所示,和前面沒有任何區別:
@Test
void testHelloPredicates() {
webClient.get()
.uri("/hello/str")
.accept(MediaType.APPLICATION_JSON)
.exchange()
// 驗證狀態
.expectStatus().isOk()
// 驗證結果,注意結果是字符串格式
.expectBody(String.class).consumeWith(result -> assertTrue(result.getResponseBody().contains(Constants.HELLO_PREFIX)));
}
@Test
void testLoadBalance() {
webClient.get()
.uri("/lbtest/str")
.accept(MediaType.APPLICATION_JSON)
.exchange()
// 驗證狀態
.expectStatus().isOk()
// 驗證結果,注意結果是字符串格式
.expectBody(String.class).consumeWith(result -> assertTrue(result.getResponseBody().contains(Constants.LB_PREFIX)));
}
- 運行單元測試類,測試通過,證明從nacos獲取配置文件成功:
寫代碼的方式配置
-
前面的幾個例子,路由信息都是寫在配置文件中的,其實還有一種方式:寫代碼配置路由,能自己寫代碼來配置,這靈活性就更強了
-
新增名為gateway-by-code的子工程,其pom.xml文件參照前面工程的即可
-
接下來的本例的重點,在配置類中增加一個RouteLocator類型的bean,通過以下代碼即可增加一個路由:
package com.bolingcavalry.gateway.cofig;
import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class RouteConfig {
@Bean
public RouteLocator customizeRoute(RouteLocatorBuilder builder) {
return builder
.routes()
.route(
// 第一個參數是路由的唯一身份
"path_route_lb",
// 第二個參數是個lambda實現,
// 設置了配套條件是按照請求路徑匹配,以及轉發地址,
// 注意lb://表示這是個服務名,要從
r -> r.path("/lbtest/**").uri("lb://provider-hello")
)
.build();
}
}
- 上述代碼只配置了一個路由,還有一個在配置文件中,這樣就能驗證代碼和配置文件能不能同時生效了:
server:
#服務端口
port: 8084
spring:
application:
name: gateway-by-code
cloud:
nacos:
discovery:
# nacos服務地址
server-addr: 127.0.0.1:8848
gateway:
routes:
- id: path_route_addr
uri: http://127.0.0.1:8082
predicates:
- Path=/hello/**
-
測試類和之前工程的一模一樣,就不占用篇幅了,依舊是兩個測試方法testHelloPredicates和testLoadBalance
-
執行單元測試可以順利通過,證明代碼配置路由沒有問題:
- 至此,負載均衡、nacos配置、代碼配置的實例咱們都嘗試過了,它們合起來會給實際生存環境的配置帶來很大的方便,希望能夠給您一些參考
缺陷和解決之道
- 上述配置方式雖多,但有一個共同的問題:每當配置變動后,Gateway應用需要重啟才能生效,這在請求不間斷的生產環境是難以接受的
- 為了讓最新的路由配置能在Gateway應用不重啟的前提下生效,接下來的文章咱們一起去探索動態路由是如何實現的
你不孤單,欣宸原創一路相伴
歡迎關注公眾號:程序員欣宸
微信搜索「程序員欣宸」,我是欣宸,期待與您一同暢游Java世界...
https://github.com/zq2599/blog_demos