歡迎訪問我的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