• 加入 spring-cloud-starter-consul-config
  • 在 bootstrap.properties | yml 中 加入相关配置
    • spring.cloud.consul.host=localhost
    • spring.cloud.consul.port=8500
    • spring.cloud.consul.config.enabled=true
    开启consul配置中心支持

Consul 中的数据怎么存

  • spring.cloud.consul.config.format=
    KEY_VALUE | YAML | PROPERTIES | FILES
    配置数据存储的形式
  • /config/应用名,profile/data
    所有的配置 在这个data节点里面
  • /config/application,profile/data
  • spring.cloud.consul.config.data-key=data
    配置key的名字,由于Consul是K/V存储,配置存储在对应K的V中

  • spring.cloud.consul.config.root=config

  • spring.cloud.consul.config.default-context=application
    指定consul配置的配置文件父路径

  • spring.cloud.consul.config.profile-separator=’,’
    置配置的分隔符

配置项变更

自动刷新配置

  • spring.cloud.consul.config.watch.enabled=true

  • spring.cloud.consul.config.watch.delay=1000
    刷新时间为每隔一秒钟

  • 单线程 ThreadPoolTaskScheduler
  • ConsulConfigAutoConfiguration.CONFIG_WATCH_TASK_SCHEDULER_NAME
    使用 ThreadPoolTaskScheduler 每隔一秒钟 去检测 看看有没有发生变化 如果有变化 就发一个event事件出来

在这里插入图片描述
需要修改的代码

bootstrap.properties

spring.application.name=waiter-service
spring.cloud.consul.host=localhost
spring.cloud.consul.port=8500
spring.cloud.consul.discovery.prefer-ip-address=true
spring.cloud.consul.config.enabled=true
#是否启用配置中心功能
spring.cloud.consul.config.format=yaml
#设置配置值的格式

pom文件

<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-cache</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-data-jpa</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-actuator</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-consul-discovery</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-consul-config</artifactId>
		</dependency>
		<dependency>
			<groupId>io.github.resilience4j</groupId>
			<artifactId>resilience4j-spring-boot2</artifactId>
			<version>0.14.1</version>
		</dependency>
		<dependency>
			<groupId>io.micrometer</groupId>
			<artifactId>micrometer-registry-prometheus</artifactId>
		</dependency>
		<dependency>
			<groupId>org.joda</groupId>
			<artifactId>joda-money</artifactId>
			<version>1.0.1</version>
		</dependency>
		<dependency>
			<groupId>org.jadira.usertype</groupId>
			<artifactId>usertype.core</artifactId>
			<version>6.0.1.GA</version>
		</dependency>
		<!-- 增加Jackson的Hibernate类型支持 -->
		<dependency>
			<groupId>com.fasterxml.jackson.datatype</groupId>
			<artifactId>jackson-datatype-hibernate5</artifactId>
			<version>2.9.8</version>
		</dependency>
		<dependency>
			<groupId>org.apache.commons</groupId>
			<artifactId>commons-lang3</artifactId>
		</dependency>
		<dependency>
			<groupId>com.h2database</groupId>
			<artifactId>h2</artifactId>
			<scope>runtime</scope>
		</dependency>
		<dependency>
			<groupId>org.projectlombok</groupId>
			<artifactId>lombok</artifactId>
			<optional>true</optional>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
	</dependencies>
	<dependencyManagement>
		<dependencies>
			<dependency>
				<groupId>org.springframework.cloud</groupId>
				<artifactId>spring-cloud-dependencies</artifactId>
				<version>${spring-cloud.version}</version>
				<type>pom</type>
				<scope>import</scope>
			</dependency>
		</dependencies>
	</dependencyManagement>

启动 consul
输入 http://localhost:8500 进入 consul 在 key/Value 中 新建一个 key/Value 为yaml格式
在这里插入图片描述

启动程序 使用 postman 进行订单的创建 我们之前在consul写入的配置 成功 配置
在这里插入图片描述

将 折扣进行调整 变成 50 控制台显示discount的变更在这里插入图片描述
在这里插入图片描述

使用 postman 继续进行测试 结果中的discount已经成功修改
在这里插入图片描述

ConsulConfigBootstrapConfiguration

@Configuration
@ConditionalOnConsulEnabled
public class ConsulConfigBootstrapConfiguration {
    public ConsulConfigBootstrapConfiguration() {
    @Configuration
    @EnableConfigurationProperties
    @Import({ConsulAutoConfiguration.class})
    @ConditionalOnProperty(
        name = {"spring.cloud.consul.config.enabled"},
        matchIfMissing = true
    protected static class ConsulPropertySourceConfiguration {
        @Autowired
        private ConsulClient consul;
        protected ConsulPropertySourceConfiguration() {
        @Bean
        @ConditionalOnMissingBean
        public ConsulConfigProperties consulConfigProperties() {
            return new ConsulConfigProperties();
        @Bean     //配置 consulPropertySourceLocator   通过 consulPropertySourceLocator 找到对应的 PropertySource
        public ConsulPropertySourceLocator consulPropertySourceLocator(ConsulConfigProperties consulConfigProperties) {
            return new ConsulPropertySourceLocator(this.consul, consulConfigProperties);

ConsulConfigAutoConfiguration

@Configuration
@ConditionalOnConsulEnabled
@ConditionalOnProperty(
    name = {"spring.cloud.consul.config.enabled"},
    matchIfMissing = true
public class ConsulConfigAutoConfiguration {
    public static final String CONFIG_WATCH_TASK_SCHEDULER_NAME = "configWatchTaskScheduler";
    public ConsulConfigAutoConfiguration() {
    @Configuration
    @ConditionalOnClass({RefreshEndpoint.class})
    protected static class ConsulRefreshConfiguration {
        protected ConsulRefreshConfiguration() {
        @Bean
        @ConditionalOnProperty(
            name = {"spring.cloud.consul.config.watch.enabled"},
            matchIfMissing = true
        //在 configWatch 中 会用到一个单线程的 ThreadPoolTaskScheduler  在创建的时候 将 name 这个常量传入
        public ConfigWatch configWatch(ConsulConfigProperties properties, ConsulPropertySourceLocator locator, ConsulClient consul, @Qualifier("configWatchTaskScheduler") TaskScheduler taskScheduler) {
            return new ConfigWatch(properties, consul, locator.getContextIndexes(), taskScheduler);
        @Bean(
            name = {"configWatchTaskScheduler"}
        @ConditionalOnProperty(
            name = {"spring.cloud.consul.config.watch.enabled"},
            matchIfMissing = true
        public TaskScheduler configWatchTaskScheduler() {
            return new ThreadPoolTaskScheduler();

ConfigWatch

public class ConfigWatch implements ApplicationEventPublisherAware, SmartLifecycle {
    private static final Log log = LogFactory.getLog(ConfigWatch.class);
    private final ConsulConfigProperties properties;
    private final ConsulClient consul;
    private final TaskScheduler taskScheduler;
    private final AtomicBoolean running;
    private LinkedHashMap<String, Long> consulIndexes;
    private ApplicationEventPublisher publisher;
    private boolean firstTime;
    private ScheduledFuture<?> watchFuture;
    public ConfigWatch(ConsulConfigProperties properties, ConsulClient consul, LinkedHashMap<String, Long> initialIndexes) {
        this(properties, consul, initialIndexes, getTaskScheduler());
    public ConfigWatch(ConsulConfigProperties properties, ConsulClient consul, LinkedHashMap<String, Long> initialIndexes, TaskScheduler taskScheduler) {
        this.running = new AtomicBoolean(false);
        this.firstTime = true;
        this.properties = properties;
        this.consul = consul;
        this.consulIndexes = new LinkedHashMap(initialIndexes);
        this.taskScheduler = taskScheduler;
    private static ThreadPoolTaskScheduler getTaskScheduler() {
        ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler();
        taskScheduler.initialize();
        return taskScheduler;
    public void setApplicationEventPublisher(ApplicationEventPublisher publisher) {
        this.publisher = publisher;
    public void start() {//在启动的时候 设置它的时间 从 properties 中 找出时间的参数(有默认的)
        if (this.running.compareAndSet(false, true)) {
            this.watchFuture = this.taskScheduler.scheduleWithFixedDelay(this::watchConfigKeyValues, (long)this.properties.getWatch().getDelay());
    public boolean isAutoStartup() {
        return true;
    public void stop(Runnable callback) {
        this.stop();
        callback.run();
    public int getPhase() {
        return 0;
    public void stop() {
        if (this.running.compareAndSet(true, false) && this.watchFuture != null) {
            this.watchFuture.cancel(true);
    public boolean isRunning() {
        return this.running.get();
    @Timed("consul.watch-config-keys")
    public void watchConfigKeyValues() {//是一个定时任务 每隔一秒钟 会跑一次  在这里面 做了一个 从 Consul 中获取值的动作
        if (this.running.get()) {
            Iterator var1 = this.consulIndexes.keySet().iterator();
            while(var1.hasNext()) {
                String context = (String)var1.next();
                if (this.properties.getFormat() != Format.FILES && !context.endsWith("/")) {
                    context = context + "/";
                try {
                    Long currentIndex = (Long)this.consulIndexes.get(context);
                    if (currentIndex == null) {
                        currentIndex = -1L;
                    log.trace("watching consul for context '" + context + "' with index " + currentIndex);
                    String aclToken = this.properties.getAclToken();
                    if (StringUtils.isEmpty(aclToken)) {
                        aclToken = null;
                    Response<List<GetValue>> response = this.consul.getKVValues(context, aclToken, new QueryParams((long)this.properties.getWatch().getWaitTime(), currentIndex));//取得kv的值 将取回来的值 进行判断 是否发生看变化  如果 变了 就publish一个RefreshEvent消息出去  推出去之后 我们就去刷新 这里 所有需要的refreshsource  加了source的这些bean 它好会被刷新掉 
                    if (response.getValue() != null && !((List)response.getValue()).isEmpty()) {
                        Long newIndex = response.getConsulIndex();
                        if (newIndex != null && !newIndex.equals(currentIndex)) {
                            if (!this.consulIndexes.containsValue(newIndex) && !currentIndex.equals(-1L)) {
                                log.trace("Context " + context + " has new index " + newIndex);
                                ConfigWatch.RefreshEventData data = new ConfigWatch.RefreshEventData(context, currentIndex, newIndex);
                                this.publisher.publishEvent(new RefreshEvent(this, data, data.toString()));
                            } else if (log.isTraceEnabled()) {
                                log.trace("Event for index already published for context " + context);
                            this.consulIndexes.put(context, newIndex);
                        } else if (log.isTraceEnabled()) {
                            log.trace("Same index for context " + context);
                    } else if (log.isTraceEnabled()) {
                        log.trace("No value for context " + context);
                } catch (Exception var8) {
                    if (this.firstTime && this.properties.isFailFast()) {
                        log.error("Fail fast is set and there was an error reading configuration from consul.");
                        ReflectionUtils.rethrowRuntimeException(var8);
                    } else if (log.isTraceEnabled()) {
                        log.trace("Error querying consul Key/Values for context '" + context + "'", var8);
                    } else if (log.isWarnEnabled()) {
                        log.warn("Error querying consul Key/Values for context '" + context + "'. Message: " + var8.getMessage());
        this.firstTime = false;
    static class RefreshEventData {
        private final String context;
        private final Long prevIndex;
        private final Long newIndex;
        RefreshEventData(String context, Long prevIndex, Long newIndex) {
            this.context = context;
            this.prevIndex = prevIndex;
            this.newIndex = newIndex;
        public String getContext() {
            return this.context;
        public Long getPrevIndex() {
            return this.prevIndex;
        public Long getNewIndex() {
            return this.newIndex;
        public boolean equals(Object o) {
            if (this == o) {
                return true;
            } else if (o != null && this.getClass() == o.getClass()) {
                ConfigWatch.RefreshEventData that = (ConfigWatch.RefreshEventData)o;
                return Objects.equals(this.context, that.context) && Objects.equals(this.prevIndex, that.prevIndex) && Objects.equals(this.newIndex, that.newIndex);
            } else {
                return false;
        public int hashCode() {
            return Objects.hash(new Object[]{this.context, this.prevIndex, this.newIndex});
        public String toString() {
            return (new ToStringCreator(this)).append("context", this.context).append("prevIndex", this.prevIndex).append("newIndex", this.newIndex).toString();

ConsulConfigProperties
在这个里面 有默认的时间参数

 public static class Watch {
        private int waitTime = 55;
        private boolean enabled = true;
        private int delay = 1000;
                    Spring Cloud Consul Config依赖加入 spring-cloud-starter-consul-config启用在 bootstrap.properties | yml 中 加入相关配置• spring.cloud.consul.host=localhost• spring.cloud.consul.port=8500• spring.cloud.consul...
				
之前使用eureka时,注册服务的ID 是随机数,eureka上不会出现同一服务多实例的问题。但是,换上了 consul 作为注册中心后,却出现同一个服务拥有多个实例的问题,上次服务挂掉之后的实例还在注册中心上挂着,每次重启多一个实例。 有什么办法去解决这个问题?答案就是自定义spring cloud consul 的注册方法,使其唯一化。 public class ServiceIdRegist...
今天有个需要新搭一个组件服务项目的时候遇到consul配置中心相关配置(host,port等配置)不生效的问题。 查了些资料和源码,如果只是将consul的配置配置在 application.properties 中的时候,这些配置不会生效。 在ConsulConfigBootstrapConfiguration类中,规定了spring.cloud.consul.config.enabled参数不设置则默认为true。 而这个值在application.proper
Spring Cloud Consul 服务注册和发现实现 Spring Cloud Kubernetes 使用,可以通过引入 org.springframework.cloud:spring-cloud-starter-consul-discovery,这个 starter 依赖于 org.springframework.cloud:spring-cloud-consul-core 和 org.springframework.cloud:spring-cloud-consul-discovery
熟能生巧,通过实战发现问题,解决问题。 在之前系列基础上,在主工程里添加一个common模块,提供 Redis 工具类, 从Consul Config 配置中心读取 Redis 配置,然后在订单服务里引用Common模块。 1、maven依赖 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</a
入口ConfigurableEnvironment environment = prepareEnvironment(listeners,applicationArguments);断点跟踪1.准备环境private ConfigurableEnvironment prepareEnvironment( SpringApplicationRunListeners listeners...
一个服务正常情况下,应该只会注册一个节点,但是出于某些原因,需要把多个节点合并为一个节点,但是不能影响到其它节点的调用,除非去通知服务调用人员,改一下调用接口.但是要把所有调用端都改完,还是得花些时间的… 这里就是以最小时间成本,合并服务. spring知识整理之服务发现 这里以consul为例,其它服务发现Eureka/k8s的api应该也都是类似的作法 基于 spring cloud /con...
Spring Cloud Consul配置的自动刷新功能是通过 org.springframework.cloud.consul.config.ConfigWatch进行实现,ConfigWatch初始化后,会调用定时器,跟服务器上面的配置文件的版本进行比较,如果版本不一致,则调用Spring 的刷新事件,触发事件刷新,否则代表配置没有变化。