Spring Cloud 之 Config與動態路由.


一、簡介

 Spring Cloud Confg 是用來為分布式系統中的基礎設施和微服務應用提供集中化的外部配置支持,它分為服務端與客戶端兩個部分。其中服務端也稱為分布式配置中心,它是一個獨立的微服務應用,用來連接配置倉庫並為客戶端提供獲取配置信息、加密/解密信息等訪問接口;而客戶端則是微服務架構中的各個微服務應用或基礎設施,它們通過指定的配置中心來管理應用資源與業務相關的配置內容,並在啟動的時候從配置中心獲取和加載配置信息。

二、Spring Config Server

搭建一個 Config Server,首先需要一個倉庫,作為分布式配置中心的存儲。這里我們選擇了 Github 作為我們的倉庫:https://github.com/JMCuixy/cloud-config-server/tree/master/config-repo

1. pom.xml

    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-config-server</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
    </dependencies>

2. application.yml

server:
  port: 7001

spring:
  application:
    name: cloud-config-server

  # 配置完成后可訪問的 url 如下,比如:http://localhost:7001/env/default
  # /{application}/{profile} [/{label}]
  # /{application}-{profile}.yml
  # /{label}/{application}-{profile}.yml
  # /{application}-{profile}.properties
  # /{label}/{application}-{profile}.properties
  cloud:
    config:
      # 為配置中心提供安全保護
      username: user
      password: password
      server:
        git:
          # 倉庫地址
          uri: https://github.com/JMCuixy/cloud-config-server.git
          # 搜索路徑
          search-paths: config-repo
        # 訪問 http://localhost:7001/actuator/health 可以獲取配置中心健康指標
        health:
          repositories:
            env:
              name: env
              profiles: default
              label: master

management:
  endpoint:
    health:
      enabled: true
      show-details: always

eureka:
  client:
    service-url:
      defaultZone: http://user:password@localhost:1111/eureka/

這里我沒有配置 Github 的 username 和 password,用的是 SSH key 的方式。

3. ConfigApplication.java

// 開啟 Spring Cloud Config 的 Server 功能
@EnableConfigServer
@EnableDiscoveryClient
@SpringBootApplication
public class ConfigApplication {

    public static void main(String[] args) {
        SpringApplication.run(ConfigApplication.class, args);
    }

}

至此,一個 Spring Cloud Config Server 就搭建完成了。上面的配置中,我們將 Config Server 注冊到 Eureka Server 中,當作整個系統服務的一部分,所以 Config Client 只要利用 Eureka 的服務發現維持與 Config Server 通信就可以了。

在Config Server 的文件系統中,每次客戶端請求獲取配置信息時,Confg Server 從 Git 倉庫中獲取最新配置到本地,然后在本地 Git 倉庫中讀取並返回。當遠程倉庫無法獲取時,直接將本地內容返回。

三、Spring Config Client

Spring Cloud Confg 的客戶端在啟動的時候,默認會從工程的 classpath 中加載配置信息並啟動應用。只有當我們配置 spring.cloud.config.uri(或者spring.cloud.config.discovery) 的時候,客戶端應用才會嘗試連接 Spring Cloud Confg 的服務端來獲取遠程配置信息並初始化 Spring 環境配置。同時,我們必須將該參數配置在bootstrap.yml、環境變量或是其他優先級高於應用 Jar 包內的配置信息中,才能正確加載到遠程配置。

1. pom.xml

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-config-client</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>

        <!-- 當連接 config-server 失敗的時候,可增加重試-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.retry</groupId>
            <artifactId>spring-retry</artifactId>
        </dependency>

        <!--配置動態刷新-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
    </dependencies>

2. bootstrap.yml 和 application.yml

  • bootstrap.yml
spring:
  application:
    # 對應配置文件規則中的 {application} 部分
    name: env
  cloud:
    config:
      name: env
      # uri: http://localhost:7001
      discovery:
        enabled: true
        service-id: cloud-config-server
      # 環境變量  
      profile: default
      # 分支
      label: master
      # config Server 配置的安全信息
      username: user
      password: password
      # 快速失敗響應(當發現 config-server 連接失敗時,就不做連接的准備工作,直接返回失敗)
      fail-fast: true
      # 失敗重試
      retry:
        # 初始重試間隔時間,毫秒
        initial-interval: 1000
        # 下一間隔的乘數
        multiplier: 1.1
        # 最大間隔時間
        max-interval: 2000
        # 最多重試次數
        max-attempts: 6

bootstrap 配置會系統會優先加載,加載優先級比 application 高。

  • application.yml
server:
  port: 7002

spring:
  application:
    name: cloud-config-client

eureka:
  client:
    service-url:
      defaultZone: http://user:password@localhost:1111/eureka/

management:
  endpoints:
    web:
      exposure:
        # 開啟指定端點
        # 配置刷新地址:POST http://127.0.0.1:7002/actuator/refresh
        include: 'refresh'

3. ConfigClientApplication.java

@EnableDiscoveryClient
@SpringBootApplication
public class ConfigClientApplication {

    public static void main(String[] args) {
        SpringApplication.run(ConfigClientApplication.class, args);
    }

}

4. 應用

接下來瞅瞅客戶端要怎么讀到服務器的配置項呢?

@RefreshScope
@RestController
public class ConfigClientAdmin {

    @Value("${from:default}")
    private String from;

    @Autowired
    private Environment environment;


    @RequestMapping("/from")
    public String from() {
        String fromEnv = environment.getProperty("from");
        return from + "_" + fromEnv;
    }
}

如上,我們可以使用 @Value 注解注入配置信息,或者使用 Environment Bean 來獲取配置項。

需要注意的是,當服務端的配置項更新的時候,客戶端並不會同步獲得更新,需要 Post 方法執行 "/actuator/refresh" 來刷新配置項。

@RefreshScope 注解使配置的內容動態化,當使用 http://127.0.0.1:7002/actuator/refresh 刷新配置的時候,會刷新帶有 @RefreshScope 的 Bean。

四、動態路由

上一篇文章 我們嘗試用 Spring Cloud Zuul 搭建了網關服務,但是我們發現路由信息都配置在 application.yml 中,這對網關的高可用是個不小的打擊,因為網關作為系統流量的路口,總不能因為改個路由信息天天重啟網關吧?所以動態路由的實現,就變得迫不及待了,好在我們現在有了 Spring Cloud Config。

首先,我們將 Spring Cloud Zuul 的路由信息,配置在 Config Server 的 env.yml 中:

zuul:
  routes:
    client-1:
      # ?:匹配任意單個數量字符;*:匹配任意多個數量字符;**:匹配任意多個數量字符,支持多級目錄
      # 使用 url 的配置沒有線程隔離和斷路器的自我保護功能,不推薦使用
      path: /client-1/**
      url: http://localhost:2222/
      # 敏感頭信息設置為空,表示不過濾敏感頭信息,允許敏感頭信息滲透到下游服務器
      sensitiveHeaders: ""
      customSensitiveHeaders: true
    client-2:
      path: /client-2/**
      serviceId: cloud-eureka-client
    # zuul.routes.<serviceid> = <path>
    cloud-eureka-client: /client-3/**
    client-4:
      path: /client-4/**
      # 請求轉發 —— 僅限轉發到本地接口
      url: forward:/local

  # Zuul 將對所有的服務都不自動創建路由規則
  ignored-services: "*"
  # 對某些 url 設置不經過路由選擇
  ignored-patterns: {"/**/world/**","/**/zuul/**"}
  # Spring Cloud Zuul在請求路由時,會過濾掉 HTTP 請求頭(Cookie、Set-Cookie、Authorization)信息中的一些敏感信息,
  sensitive-headers: {"Cookie", "Set-Cookie", "Authorization"}
  # 網關在進行路由轉發時為請求設置 Host 頭信息(保持在路由轉發過程中 host 頭信息不變)
  add-host-header: true
  # 請求轉發時加上 X-Forwarded-*頭域
  add-proxy-headers: true
  # 是否開啟重試,默認關閉
  retryable: true
  # 通過 /zuul 路徑訪問的請求會繞過 dispatcherServlet, 被 Zuu1Servlet 處理,主要用來應對處理大文件上傳的情況。
  servlet-path: /zuul
  # 禁用某個過濾器 zuul.<SimpleClassName>.<filterTye>.disable=true
  TokenFilter:
    pre:
      disable: true

然后,我們將網關服務注冊為 Config Client(配置項與上面類似,就不贅述了),從 Config Server 獲取路由信息:

@EnableZuulProxy
@EnableDiscoveryClient
@SpringBootApplication
public class DynamicRouteApplication {

    public static void main(String[] args) {
        SpringApplication.run(DynamicRouteApplication.class, args);
    }

    /**
     * 刷新地址:POST http://127.0.0.1:5006/actuator/refresh
     * 路由查看地址:GET http://127.0.0.1:5006/actuator/routes
     *
     * @return
     */
    @Bean
    @Primary
    //該注解來使 zuul 的配置內容動態化
    @RefreshScope
    @ConfigurationProperties(prefix = "zuul")
    public ZuulProperties zuulProperties() {
        return new ZuulProperties();
    }

}

這樣,就把我們的路由信息交給 Config Server 去管理了~~

五、Spring Cloud Bus

上面的內容我們學習到:當 Config Server 配置變更了以后,Config Client 通過執行 “/actuator/refresh” 來刷新配置項。一兩個實例的話,倒不算什么。實例一旦多了,這將是一個非常繁瑣的工作。怎么辦呢?就要說到 Spring Cloud Bus。

微服務架構的系統中,我們通常會使用輕量級的消息代理來構建一個共用的消息主題讓系統中所有微服務實例都連接上來,由於該主題中產生的消息會被所有實例監聽和消費,所以我們稱它為消息總線。在總線上的各個實例都可以方便地廣播一些需要讓其他連接在該主題上的實例都知道的消息,例如配置信息的變更或者其他一些管理操作等。Bus 就是 Spring Cloud 中的消息總線。

那么 Spring Cloud Bus 怎么刷新我們的配置項呢?

  1. 當 Config Server 配置項發生變更的時候,發送一個消息到消息代理的 Topic 下。
  2. Config Client 訂閱 Topic 接收到消息后,主動執行 “/actuator/refresh” 刷新配置。

Spring Cloud Bus 的集成(基於 RabbitMQ 消息代理)也非常簡單,只需要我們在 Config Server 中引用:

<dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-bus-amqp</artifactId>
 </dependency>

然后,在 application.yml 中配置 RabbitMQ ,並暴露 “bus-refresh” 端點:

management:
  endpoints:
    web:
      exposure:
        # 執行 http://127.0.0.1:7001/actuator/bus-refresh,把配置內容修改的消息發布到服務總線。
        # 客戶端收到訂閱消息,自動執行 /actuator/refresh,刷新配置
        include: bus-refresh

客戶端的配置和服務端類似,也只要引入 jar 包,配置 RabbitMQ 即可(不用暴露 “bus-refresh” 端點)。

篇幅有限,不細講了,大家可以看我 GitHub 上的內容,包括 RabbitMQ 的集成和 Kafka 的集成。

六、附錄

SpringBoot 版本號:2.1.6.RELEASE

SpringCloud 版本號:Greenwich.RELEASE

演示源代碼 :https://github.com/JMCuixy/spring-cloud-demo

內容參考:《Spring Cloud 微服務實戰》


免責聲明!

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



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