Spring Cloud Consul 动态配置
简介
如同 Nacos 一样 Consul 也支持配置存储,并且也提供了配置自动更新的机制。
项目搭建
创建 Spring Cloud 项目,然后按照如下配置引入依赖包
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 
 | ext {set('springCloudVersion', "2022.0.0-M5")
 }
 
 dependencies {
 implementation 'org.springframework.boot:spring-boot-starter-web'
 implementation 'org.springframework.cloud:spring-cloud-starter-consul-config'
 implementation 'org.springframework.cloud:spring-cloud-starter-consul-discovery'
 implementation('org.springframework.cloud:spring-cloud-starter-bootstrap')
 compileOnly 'org.projectlombok:lombok'
 developmentOnly 'org.springframework.boot:spring-boot-devtools'
 annotationProcessor 'org.springframework.boot:spring-boot-configuration-processor'
 annotationProcessor 'org.projectlombok:lombok'
 testImplementation 'org.springframework.boot:spring-boot-starter-test'
 }
 
 dependencyManagement {
 imports {
 mavenBom "org.springframework.cloud:spring-cloud-dependencies:${springCloudVersion}"
 }
 }
 
 | 
编辑 Spring 配置文件 application.yaml:
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 
 | spring:application:
 name: <name>
 cloud:
 consul:
 host: ${CONSUL_HOST:localhost}
 port: ${CONSUL_PORT:8500}
 discovery:
 prefer-ip-address: true
 tags: version=1.0
 instance-id: ${spring.application.name}:${spring.cloud.client.hostname}:${spring.cloud.client.ip-address}:${server.port}
 healthCheckInterval: 15s
 
 server:
 port: 8080
 error:
 include-message: always
 include-exception: true
 servlet:
 encoding:
 charset: UTF-8
 
 | 
编辑引入配置文件 bootstrap.yaml:
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 
 | spring:cloud:
 consul:
 config:
 enabled: true
 defaultContext: <name>
 profileSeparator: '-'
 prefixes: config
 format: properties
 watch:
 enabled: true
 
 | 
注:此处支持的 format 有 yaml, properties, key_value, files 可以根据实际情况进行选用。
编辑 Spring 主类并添加如下注解:
| 12
 3
 4
 5
 6
 7
 8
 9
 
 | import org.springframework.boot.autoconfigure.SpringBootApplication;import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
 import org.springframework.scheduling.annotation.EnableScheduling;
 
 @EnableDiscoveryClient
 @EnableScheduling
 @SpringBootApplication
 public class DynApplication {
 }
 
 | 
编辑 Spring 配置类
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 
 | import lombok.Getter;import lombok.Setter;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.boot.context.properties.ConfigurationProperties;
 import org.springframework.cloud.context.config.annotation.RefreshScope;
 import org.springframework.context.annotation.Configuration;
 
 @Slf4j
 @Getter
 @Setter
 @RefreshScope
 @Configuration
 @ConfigurationProperties(prefix = "my")
 public class MyProperties {
 
 private String prop;
 
 }
 
 | 
编辑调试接口类:
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 
 | import com.example.dyn.conf.MyProperties;import jakarta.annotation.Resource;
 import org.springframework.http.HttpEntity;
 import org.springframework.web.bind.annotation.*;
 
 @RestController
 @RequestMapping("/demo")
 public class DemoController {
 
 @Value("http://${spring.cloud.consul.host}:${spring.cloud.consul.port}/v1/kv/${spring.cloud.consul.config.prefixes}/${spring.cloud.consul.config.defaultContext}-${spring.profiles.active}/data")
 String consulUrl;
 
 @Resource
 MyProperties myProperties;
 
 @GetMapping
 public HttpEntity<String> get() {
 return new HttpEntity<>(myProperties.getProp());
 }
 
 @PutMapping
 public HttpEntity<String> put(@RequestParam String prop) {
 RestTemplate restTemplate = new RestTemplate();
 HttpHeaders headers = new HttpHeaders();
 headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
 MultiValueMap<String, String> content = new LinkedMultiValueMap<>();
 content.put("my.prop", Collections.singletonList(prop));
 HttpEntity<MultiValueMap<String, String>> entity = new HttpEntity<>(content, headers);
 ResponseEntity<Object> response = restTemplate.exchange(consulUrl, HttpMethod.PUT, entity, Object.class);
 if (response.getStatusCode().is2xxSuccessful()) {
 return new HttpEntity<>(prop);
 } else {
 throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "consul set error");
 }
 }
 
 }
 
 | 
启动程序即可
流程测试
如需手动编辑可以访问 consul webUI 的 Key/Value 选项中编辑配置向相关配置:
配置目录如下:
配置内容如下:
也可以通过 REST API 进行编辑,请求样例如下:
request| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 
 | ### 写入配置项PUT http://localhost:8080/demo?prop=edit
 
 ### 获取配置项
 GET http://localhost:8080/demo
 
 ### 使用 Consul HTTP 接口读取配置项
 GET http://localhost:8500/v1/kv/config/dyn-default/data
 
 ### 使用 Consul HTTP 接口编辑配置项
 PUT http://localhost:8500/v1/kv/config/dyn-default/data
 Content-Type: application/x-www-form-urlencoded
 
 my.prop=idea-http
 
 | 
 
注:配置项的空间上限是 512 kb。
配置变更监听
在配置变更的时候可以采用如下的监听器,针对变更的配置完成业务逻辑。
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 
 | import lombok.extern.slf4j.Slf4j;import org.springframework.cloud.endpoint.event.RefreshEvent;
 import org.springframework.context.ApplicationListener;
 import org.springframework.stereotype.Component;
 
 @Slf4j
 @Component
 public class CustomRefreshEventListener implements ApplicationListener<RefreshEvent> {
 
 @Override
 public void onApplicationEvent(RefreshEvent event) {
 log.error("checked");
 }
 
 }
 
 | 
参考资料
Spring Cloud Consul 官方文档
Consul KV store HTTP 接口手册