<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
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
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() {
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() {
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));
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 的刷新事件,触发事件刷新,否则代表配置没有变化。