有別於傳統設定檔方式,將設定存放到設定中心,當有修改異動,可以立即反應在 AP 上,而服務能夠在不重啟的狀態下,重新套用變動後的設定值, EP 14 增加了相依套件及 GlobalSetting 範例,並透過 JUnit 5 單元測試來驗證產出結果。
Table of Contents
Toggle前言
Consul 是一個服務網格解決方案,提供了一個功能齊全的控制平面,具有服務發現、中心配置、健康檢測功能。這些功能中的每一項都可以單獨使用,也可以一起使用來構建一個完整的服務網格。
Spring Cloud Consul
檔案目錄
./
+- build.gradle
+- src
+- main
+- resources
| +- bootstrap.properties
+- java
| +- org
| +- ruoxue
| +- spring_boot_168
| +- config
| +- model
| +- GlobalSetting.java
+- test
+- java
+- org
+- ruoxue
+- spring_boot_168
+- config
+- model
+- GlobalSettingTest.java
Gradle
build.gradle
增加 Spring Cloud Starter Consul 。
修改完後,點右鍵,Gradle -> Refresh Gradle Project 。
buildscript {
group 'org.ruoxue.spring-boot-168'
version = '0.0.1-SNAPSHOT'
ext {
springBootVersion = '2.1.7.RELEASE'
springCloudVersion = '2.1.2.RELEASE'
springCloudDependenciesVersion = 'Greenwich.RELEASE'
}
}
plugins {
id 'java-library'
id 'eclipse'
id 'org.springframework.boot' version '2.1.7.RELEASE'
id 'io.spring.dependency-management' version '1.0.6.RELEASE'
}
dependencyManagement {
imports {
mavenBom("org.springframework.boot:spring-boot-dependencies:${springBootVersion}")
mavenBom("org.springframework.cloud:spring-cloud-dependencies:${springCloudDependenciesVersion}")
}
}
dependencies {
implementation "org.springframework.cloud:spring-cloud-starter-consul-discovery:${springCloudVersion}"
implementation "org.springframework.cloud:spring-cloud-starter-consul-config:${springCloudVersion}"
}
設定
bootstrap.properties
增加 Consul 設定。
spring.application.name=ruoxue-spring-boot-168
spring.cloud.consul.host=127.0.0.1
spring.cloud.consul.port=8500
spring.cloud.consul.discovery.enabled=false
spring.cloud.consul.config.enabled=true
spring.cloud.consul.config.watch.enabled=true
spring.cloud.consul.config.prefix=config
spring.cloud.consul.config.profile-separator=','
spring.cloud.consul.retry.enabled=true
spring.cloud.consul.retry.initial-interval=1000
spring.cloud.consul.retry.max-attempts=6
spring.cloud.consul.retry.max-interval=2000
spring.cloud.consul.retry.multiplier=1.1
管理後台
建立 Key / Value,要按照 bootstrap.properties 中的設定,依序建置,這點非常重要。
spring.cloud.consul.config.prefix=config
spring.application.name=ruoxue-spring-boot-168
目錄:/config/ruoxue-spring-boot-168/global-setting/
key:json
value:如下

GlobalSetting.java
新增檔案,增加 @RefreshScope,開啟功能。
package org.ruoxue.spring_boot_168.config.model;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.context.annotation.Configuration;
import com.google.gson.Gson;
import lombok.AllArgsConstructor;
import lombok.NoArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
@Configuration
@ConfigurationProperties("global-setting")
@RefreshScope
@AllArgsConstructor
@NoArgsConstructor
@Getter
@Setter
@Builder
public class GlobalSetting {
private static final Gson gson = new Gson();
/** json 內容 */
private String json;
/** 預設每頁筆數 */
private int defaultPageSize;
// --------------------------------------------------------
// JWT
// --------------------------------------------------------
/** token 密鑰 */
private String tokenSecret;
/** 到期毫秒 */
private long expireTime;
/** 刷新到期毫秒 */
private long refreshExpireTime;
/** 發行者 */
private String issuer;
public void setJson(String json) {
this.json = json;
GlobalSetting setting = fromJson(json);
this.setDefaultPageSize(setting.getDefaultPageSize());
this.setTokenSecret(setting.getTokenSecret());
this.setExpireTime(setting.getExpireTime());
this.setRefreshExpireTime(setting.getRefreshExpireTime());
this.setIssuer(setting.getIssuer());
}
public static GlobalSetting fromJson(String json) {
GlobalSetting result = null;
if (json != null && !json.equals("")) {
result = gson.fromJson(json, GlobalSetting.class);
}
return result;
}
public String toString() {
ToStringBuilder builder = new ToStringBuilder(this, ToStringStyle.JSON_STYLE);
builder.appendSuper(super.toString());
builder.append("defaultPageSize", defaultPageSize);
builder.append("tokenSecret", tokenSecret);
builder.append("expireTime", expireTime);
builder.append("refreshExpireTime", refreshExpireTime);
builder.append("issuer", issuer);
return builder.toString();
}
}
測試
GlobalSettingTest.java
新增單元測試,驗證是否符合預期 。
package org.ruoxue.spring_boot_168.config.model;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.ruoxue.spring_boot_168.Application;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit.jupiter.SpringExtension;
@ExtendWith(SpringExtension.class)
@SpringBootTest(classes = Application.class)
public class GlobalSettingTest {
@Autowired
private GlobalSetting globalSetting;
@Test
public void globalSetting() {
System.out.println(globalSetting);
assertNotNull(globalSetting);
int defaultPageSize = globalSetting.getDefaultPageSize();
assertEquals(50, defaultPageSize);
String tokenSecret = globalSetting.getTokenSecret();
assertEquals("19816f04bf6089c0df8487871eb02d36", tokenSecret);
}
}
globalSetting
測試方法上點右鍵執行 Run As -> JUnit Test ,查看 console。
2022-05-19T21:29:13.746+0800 [main] INFO PropertySourceBootstrapConfiguration#initialize:98 - Located property source: CompositePropertySource {name='consul', propertySources=[ConsulPropertySource {name='config/ruoxue-spring-boot-168/'}, ConsulPropertySource {name='config/application/'}]}
2022-05-19T21:29:19.947+0800 [main] INFO ConsulServiceRegistry#register:62 - Registering service with consul: NewService{id='ruoxue-spring-boot-168-10000', name='ruoxue-spring-boot-168', tags=[secure=false], address='cheng-pc', meta=null, port=10000, enableTagOverride=null, check=Check{script='null', interval='10s', ttl='null', http='http://cheng-pc:10000/actuator/health', method='null', header={}, tcp='null', timeout='null', deregisterCriticalServiceAfter='null', tlsSkipVerify=null, status='null'}, checks=null}
{"defaultPageSize":50,"tokenSecret":"19816f04bf6089c0df8487871eb02d36","expireTime":259200000,"refreshExpireTime":259200000,"issuer":"ruoxue-spring-boot-168"}
心得分享
一般而言,修改設定,都需重啟 JVM,雖然有些程式庫可以偵測檔案變化而重新載入,不過當採分散式架構時,則會有許多服務都需要同時佈署新的設定檔,因此提供配置中心的作法,將設定統一集中於 Data Center,並且在不重啟 JVM 的狀況下,重新載入變動後的設定。