Spring Cloud Gateway實戰之二:更多路由配置方式


歡迎訪問我的GitHub

https://github.com/zq2599/blog_demos

內容:所有原創文章分類匯總及配套源碼,涉及Java、Docker、Kubernetes、DevOPS等;

本篇概覽

  • 本文是《Spring Cloud Gateway實戰》系列的第二篇,通過前文咱們了解到Spring Cloud Gateway的核心是路由配置,然后在本地application.yml中配置了一條路由,但這種修改本地配置文件的方式缺乏靈活性,未必能滿足靈活多變的業務需求,因此,本篇的目的就是找出本地配置之外的其他配置方式來,滿足各種實際需求;
  • 總的來說以下三種方式都是常用的:
  1. 目標地址支持用服務名(取代之前的IP+端口);
  2. 支持在nacos上配置;
  3. 支持寫代碼的方式配置;
  • 另外還有一種更加靈活的配置方式:動態代理,因為涉及到不少的代碼所以會單獨出一篇文章詳細介紹

源碼下載

名稱 鏈接 備注
項目主頁 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/**
  • 測試類和之前工程的一模一樣,就不占用篇幅了,依舊是兩個測試方法testHelloPredicatestestLoadBalance

  • 執行單元測試可以順利通過,證明代碼配置路由沒有問題:

在這里插入圖片描述

  • 至此,負載均衡、nacos配置、代碼配置的實例咱們都嘗試過了,它們合起來會給實際生存環境的配置帶來很大的方便,希望能夠給您一些參考

缺陷和解決之道

  • 上述配置方式雖多,但有一個共同的問題:每當配置變動后,Gateway應用需要重啟才能生效,這在請求不間斷的生產環境是難以接受的
  • 為了讓最新的路由配置能在Gateway應用不重啟的前提下生效,接下來的文章咱們一起去探索動態路由是如何實現的

你不孤單,欣宸原創一路相伴

  1. Java系列
  2. Spring系列
  3. Docker系列
  4. kubernetes系列
  5. 數據庫+中間件系列
  6. DevOps系列

歡迎關注公眾號:程序員欣宸

微信搜索「程序員欣宸」,我是欣宸,期待與您一同暢游Java世界...
https://github.com/zq2599/blog_demos


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM