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 的配置再訪問程序,可以看到配置會自動更新
