Spring Cloud (三):配置中心 Spring Cloud Config 和 Consul


Spring Boot 的配置文件一般是放在 application.properties 或 application.yml,修改配置需要重啟程序,而且每個程序管理自己的配置文件,實際應用不大方便

配置中心的好處

  • 統一管理所有程序的配置
  • 多環境,比如開發環境,生產環境,可以為不同環境維護不同的配置
  • 權限管理,一個用戶只能讀寫部分配置
  • 配置格式校驗,防止用戶配錯
  • 配置的版本控制和回滾
  • 灰度發布,指定配置只在部分機器起作用(即可以先在部分機器驗證,再推廣到所有機器)
  • 配置的實時推送,即可以不重啟程序就讓改變的配置起作用

這些功能在不同的配置中心的支持度會不一樣

Spring Cloud 最早的配置中心使用 Config,后來又開發了 Consul(即可以做配置中心又可以做服務發現),也有一些公司自己開源的,比如攜程的 Apollo,比如阿里的 Nacos

Spring Cloud Config

Config 默認使用 git 來存儲配置,除此外也可以用 svn,數據庫,本地文件系統

(1) 創建 Config Server

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

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-config-server</artifactId>
</dependency>
## bootstrap.yml
spring:
  application:
    name: config-single-server
  cloud:
     config:
        server:
          git:
            uri: https://github.com/spring-cloud-samples/config-repo
            username: xxxxxx
            password: xxxxxx
            default-label: master       # 分支
            search-paths: config-path   # 配置文件所在的根目錄
## application.yml
server:
  port: 3301
@SpringBootApplication
@EnableConfigServer
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

啟動后可以訪問的接口格式如下

/{application}/{profile}[/{label}]
/{application}-{profile}.yml
/{label}/{application}-{profile}.yml
/{application}-{profile}.properties
/{label}/{application}-{profile}.properties

application 是 app 的名字,profile 是環境,label 是分支(可選,默認是 master),比如

http://localhost:3301/my-app/dev/master
http://localhost:3301/my-app/prod
http://localhost:3301/my-app-dev.yml
http://localhost:3301/my-app-prod.yml
http://localhost:3301/master/my-app-prod.yml

Config Server 會到 https://github.com/spring-cloud-samples/config-repo 這個 repo 下的 config-path 找到相應的配置文件比如 my-app-prod.yml


(2) 創建應用

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

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

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
# bootstrap.yml
spring:
  profiles:
    active: dev

---
spring:
  profiles: prod
  application:
    name: my-app
  cloud:
     config:
       uri: http://localhost:3301
       label: master
       profile: prod

---
spring:
  profiles: dev
  application:
    name: my-app
  cloud:
     config:
       uri: http://localhost:3301
       label: master
       profile: dev
# application.yml
server:
  port: 3302
management:
  endpoint:
    shutdown:
      enabled: false
  endpoints:     # 打開 refresh 接口,允許自動刷新配置
    web:
      exposure:
        include: "*"

data:
  env: xxxx
  user:
    username: xxxx
    password: xxxx
@Component
@Data
@ConfigurationProperties(prefix = "data")
public class Data {
    public static class UserInfo {
        private String username;
        private String password;

        public String getUsername() {
            return username;
        }
        public void setUsername(String username) {
            this.username = username;
        }
        public String getPassword() {
            return password;
        }
        public void setPassword(String password) {
            this.password = password;
        }
        @Override
        public String toString() {
            return "UserInfo{" +
                    "username='" + username + '\'' +
                    ", password='" + password + '\'' +
                    '}';
        }
    }

    private String env;
    private UserInfo user;
}
@RestController
@RefreshScope
public class Controller {
    @Autowired
    private Data data;

    @GetMapping(value = "show")
    public Object show(){
        return data;
    }
}

訪問 http://localhost:3302/show 可以看到返回的是 git 上存儲的配置


(3) 自動刷新

可以看到 @RefreshScope 添加到了 Controller 上,這樣 Controller 使用的 Data 配置可以無需重啟就更新

在 git 上修改配置后,通過發送 POST 請求到 http://localhost:3302/actuator/refresh 就可以觸發自動更新

注意如果是通過 @Value 獲取的配置,無法得到自動更新

Config 原生不支持配置的實時推送,如果每次改動都要發 POST 請求給應用就太麻煩了,尤其配置的應用多的話,為此如果是使用 git 做配置存儲的話,可以使用 git 的 WebHook 功能,將每個應用的 actuator/refresh 地址添加到 git 的 WebHook,這樣當存儲在 git 上的配置有改動時,git 就會發送 POST 請求讓每個應用刷新配置

需要給每個應用注冊 WebHook 還是比較麻煩,可以利用 Spring Cloud Bus 實現訂閱通知功能,Spring Cloud Bus 的底層需要一個消息隊列,有 RabbitMQ 和 Kafka 可以選擇

假設已經有了一個 RabbitMQ 隊列,只需要在應用端加入以下配置

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-bus-amqp</artifactId>
</dependency>
spring:
  rabbitmq:
    host: localhost
    port: 5672
    username: guest
    password: guest

假設有多個應用,這時只要將其中一個的 actuator/bus-refresh 接口添加到 git 的 WebHook,這樣配置改動會發送 POST 請求到這個應用,這個應用會自動到 Config Server 拉取最新的配置,同時這個應用會通知 Bus,而 Bus 在收到通知后,會發請求給其他所有應用的 actuator/bus-refresh 接口,其他應用又會自動到 Config Server 拉取數據

但這樣會有一個應用承擔着通知 Bus 的責任,這樣不是很合適,可以將 Config Server 也添加 Bus,只將 Config Server 添加 WebHook,這樣變成由 Config Server 收到 WebHook 的請求,並通知 Bus


(4) 結合 Eureka

假設 Eureka 已經啟動,並且 Config 已經注冊到 Eureka 名字為 config-server

這樣應用需要引入 Eureka Client,配置 Eureka 信息,同時不再配置 Config 的 URI,而是配置 Config 的 Discovery 信息

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
eureka:
  client:
    serviceUrl:
      register-with-eureka: true
      fetch-registry: true
      defaultZone: http://localhost:8761/eureka/

spring:
  profiles:
    active: dev
---
spring:
  profiles: prod
  application:
    name: config-client
  cloud:
    config:
      label: master
      profile: prod
      discovery:
        enabled: true
        service-id: config-server
---
spring:
  profiles: dev
  application:
    name: config-client
  cloud:
    config:
      label: master
      profile: dev
      discovery:
        enabled: true
        service-id: config-server
@SpringBootApplication
@EnableEurekaClient
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

訪問啟動的 show 接口,同樣可以得到 git 的配置

Consul

Consul 是一個一站式的組件,集成了不少功能,包括服務發現和配置中心

consul 的安裝參考 Spring Cloud (二):服務發現 Eureka 和 Consul

現在實現一個可以自動獲取配置的應用

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-consul-config</artifactId>
</dependency>
# bootstrap.yml
spring:
  application:
    name: consul-client-service-1
  cloud:
    consul:
      host: localhost    # consul 的 ip 和端口
      port: 8500
      config:
        enabled: true
        format: yaml     # 有四種格式:yaml/files/properties/key-value, 默認 key-value
        prefix: config   # 存在 consul 上的目錄,默認就是 config
        default-context: consul-client-service-1    # 存在 consul 上的應用名字,默認是 spring.application.name
        data-key: data   # 存在 consul 上的配置的 key,對應的 value 就相等於 application.yaml 的所有內容
        watch:
          enabled: true  # 啟用配置自動刷新,默認 true
          delay: 10000   # 刷新頻率,單位毫秒,默認 1000
          wait-time: 30  # 查詢等待的時間,單位秒,默認 55
# application.yml
spring:
  profiles:
    active: dev   # 這個值同樣會在 consul 上用到
                  # consul 的路徑應該是 /config/consul-client-service-1,dev/data

server:
  port: 9000

在 Consul 上選擇 KEY/VALUE 然后創建 key,名字為 /config/consul-client-service-1,dev/data,注意最后的 data 后面沒有符號 / 不然會被當成目錄,並且寫上對應的配置內容

database-host: 192.168.1.100

user:
  name: Alice
  sex: female
  age: 30

如下圖

然后在程序中使用配置

@EnableScheduling    // 用於定時拉取配置
@SpringBootApplication
public class DemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }
}
@RefreshScope     // 用於自動刷新
@RestController
@RequestMapping("/api/v1")
public class DemoController {
    @Value("${database-host}")
    private String dbHost;

    @Autowired
    private User user;

    @GetMapping(value = "db")
    public String getDB(){
        return dbHost;
    }

    @GetMapping(value = "user")
    public Object getUser(){
        return user;
    }
}
@Component
@EnableConfigurationProperties
@ConfigurationProperties(prefix = "user")
public class User {
    private String name;
    private String sex;
    private int age

    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name= name;
    }

    public String getSex() {
        return sex;
    }
    public void setSex(String sex) {
        this.sex= sex;
    }

    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age= age;
    }
}

啟動程序,訪問程序,可以看到返回的是 Consul 上的配置信息

修改 Consul 的配置再訪問程序,可以看到配置會自動更新




免責聲明!

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



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