Spring Cloud 之 Config與動態路由.

JMCui發表於2019-07-26

一、簡介

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

二、Spring Config Server

搭建一個 Config Server,首先需要一個倉庫,作為分散式配置中心的儲存。這裡我們選擇了 Github 作為我們的倉庫:https://github.com/JMCuixy/cloud-config-server/tree/master/config-repo
Spring Cloud 之 Config與動態路由.

1. pom.xml

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

        <!--啟動 security 保護,不需要可不新增-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</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
            env-dev:
              name: env-dev
              profiles: dev
              label: master
            env-test:
              name: env-test
              profiles: test
              label: master
            env-prod:
              name: env-prod
              profiles: prod
              label: master

  # 提供 security 保護
  security:
    user:
      name: user
      password: password

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 去管理了~~

演示原始碼 :https://github.com/JMCuixy/spring-cloud-demo

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

相關文章