Spring Cloud Consul - Spring Boot 168 EP 14

Spring Cloud Consul – Spring Boot 168 EP 14

有別於傳統設定檔方式,將設定存放到設定中心,當有修改異動,可以立即反應在 AP 上,而服務能夠在不重啟的狀態下,重新套用變動後的設定值, EP 14 增加了相依套件及 GlobalSetting 範例,並透過 JUnit 5 單元測試來驗證產出結果。

前言

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:如下

Consul GlobalSetting

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 的狀況下,重新載入變動後的設定。

發佈留言