Moves ConfigWatch from @Scheduled to TaskScheduler
fixes gh-399
This commit is contained in:
@@ -218,10 +218,12 @@ spring:
|
||||
|
||||
The Consul Config Watch takes advantage of the ability of consul to https://www.consul.io/docs/agent/watches.html#keyprefix[watch a key prefix]. The Config Watch makes a blocking Consul HTTP API call to determine if any relevant configuration data has changed for the current application. If there is new configuration data a Refresh Event is published. This is equivalent to calling the `/refresh` actuator endpoint.
|
||||
|
||||
To change the frequency of when the Config Watch is called change `spring.cloud.consul.config.watch.delay`. The default value is 1000, which is in milliseconds.
|
||||
To change the frequency of when the Config Watch is called change `spring.cloud.consul.config.watch.delay`. The default value is 1000, which is in milliseconds. The delay is the amount of time after the end of the previous invocation and the start of the next.
|
||||
|
||||
To disable the Config Watch set `spring.cloud.consul.config.watch.enabled=false`.
|
||||
|
||||
The watch uses a Spring `TaskScheduler` to schedule the call to consul. By default it is a `ThreadPoolTaskScheduler` with a `poolSize` of 1. To change the `TaskScheduler`, create a bean of type `TaskScheduler` named with the `ConsulConfigAutoConfiguration.CONFIG_WATCH_TASK_SCHEDULER_NAME` constant.
|
||||
|
||||
[[spring-cloud-consul-config-format]]
|
||||
=== YAML or Properties with Config
|
||||
|
||||
|
||||
@@ -16,6 +16,12 @@
|
||||
|
||||
package org.springframework.cloud.consul.config;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import com.ecwid.consul.v1.ConsulClient;
|
||||
import com.ecwid.consul.v1.QueryParams;
|
||||
import com.ecwid.consul.v1.Response;
|
||||
@@ -23,41 +29,51 @@ import com.ecwid.consul.v1.kv.model.GetValue;
|
||||
import io.micrometer.core.annotation.Timed;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.cloud.endpoint.event.RefreshEvent;
|
||||
import org.springframework.context.ApplicationEventPublisher;
|
||||
import org.springframework.context.ApplicationEventPublisherAware;
|
||||
import org.springframework.context.SmartLifecycle;
|
||||
import org.springframework.core.style.ToStringCreator;
|
||||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
import org.springframework.scheduling.TaskScheduler;
|
||||
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import java.io.Closeable;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import static org.springframework.cloud.consul.config.ConsulConfigProperties.Format.FILES;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
public class ConfigWatch implements Closeable, ApplicationEventPublisherAware {
|
||||
public class ConfigWatch implements ApplicationEventPublisherAware, SmartLifecycle {
|
||||
|
||||
private static final Log log = LogFactory.getLog(ConfigWatch.class);
|
||||
|
||||
private final ConsulConfigProperties properties;
|
||||
private final ConsulClient consul;
|
||||
private LinkedHashMap<String, Long> consulIndexes;
|
||||
private final TaskScheduler taskScheduler;
|
||||
private final AtomicBoolean running = new AtomicBoolean(false);
|
||||
private ApplicationEventPublisher publisher;
|
||||
private boolean firstTime = true;
|
||||
private ScheduledFuture<?> watchFuture;
|
||||
|
||||
public ConfigWatch(ConsulConfigProperties properties, ConsulClient consul, LinkedHashMap<String, Long> initialIndexes) {
|
||||
this(properties, consul, initialIndexes, getTaskScheduler());
|
||||
}
|
||||
|
||||
private static ThreadPoolTaskScheduler getTaskScheduler() {
|
||||
ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler();
|
||||
taskScheduler.initialize();
|
||||
return taskScheduler;
|
||||
}
|
||||
|
||||
public ConfigWatch(ConsulConfigProperties properties, ConsulClient consul, LinkedHashMap<String, Long> initialIndexes,
|
||||
TaskScheduler taskScheduler) {
|
||||
this.properties = properties;
|
||||
this.consul = consul;
|
||||
this.consulIndexes = new LinkedHashMap<>(initialIndexes);
|
||||
this.taskScheduler = taskScheduler;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -65,12 +81,42 @@ public class ConfigWatch implements Closeable, ApplicationEventPublisherAware {
|
||||
this.publisher = publisher;
|
||||
}
|
||||
|
||||
@PostConstruct
|
||||
@Override
|
||||
public void start() {
|
||||
this.running.compareAndSet(false, true);
|
||||
if (this.running.compareAndSet(false, true)) {
|
||||
this.watchFuture = this.taskScheduler.scheduleWithFixedDelay(this::watchConfigKeyValues,
|
||||
this.properties.getWatch().getDelay());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAutoStartup() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop(Runnable callback) {
|
||||
this.stop();
|
||||
callback.run();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPhase() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop() {
|
||||
if (this.running.compareAndSet(true, false) && this.watchFuture != null) {
|
||||
this.watchFuture.cancel(true);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRunning() {
|
||||
return this.running.get();
|
||||
}
|
||||
|
||||
@Scheduled(fixedDelayString = "${spring.cloud.consul.config.watch.delay:1000}")
|
||||
@Timed(value ="consul.watch-config-keys")
|
||||
public void watchConfigKeyValues() {
|
||||
if (this.running.get()) {
|
||||
@@ -138,11 +184,6 @@ public class ConfigWatch implements Closeable, ApplicationEventPublisherAware {
|
||||
firstTime = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
this.running.compareAndSet(true, false);
|
||||
}
|
||||
|
||||
static class RefreshEventData {
|
||||
private final String context;
|
||||
private final Long prevIndex;
|
||||
|
||||
@@ -18,12 +18,15 @@ package org.springframework.cloud.consul.config;
|
||||
|
||||
import com.ecwid.consul.v1.ConsulClient;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.cloud.consul.ConditionalOnConsulEnabled;
|
||||
import org.springframework.cloud.endpoint.RefreshEndpoint;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.scheduling.TaskScheduler;
|
||||
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
@@ -33,14 +36,23 @@ import org.springframework.context.annotation.Configuration;
|
||||
@ConditionalOnProperty(name = "spring.cloud.consul.config.enabled", matchIfMissing = true)
|
||||
public class ConsulConfigAutoConfiguration {
|
||||
|
||||
public static final String CONFIG_WATCH_TASK_SCHEDULER_NAME = "configWatchTaskScheduler";
|
||||
|
||||
@Configuration
|
||||
@ConditionalOnClass(RefreshEndpoint.class)
|
||||
protected static class ConsulRefreshConfiguration {
|
||||
@Bean
|
||||
@ConditionalOnProperty(name = "spring.cloud.consul.config.watch.enabled", matchIfMissing = true)
|
||||
public ConfigWatch configWatch(ConsulConfigProperties properties,
|
||||
ConsulPropertySourceLocator locator, ConsulClient consul) {
|
||||
return new ConfigWatch(properties, consul, locator.getContextIndexes());
|
||||
ConsulPropertySourceLocator locator, ConsulClient consul,
|
||||
@Qualifier(CONFIG_WATCH_TASK_SCHEDULER_NAME) TaskScheduler taskScheduler) {
|
||||
return new ConfigWatch(properties, consul, locator.getContextIndexes(), taskScheduler);
|
||||
}
|
||||
|
||||
@Bean(name = CONFIG_WATCH_TASK_SCHEDULER_NAME)
|
||||
@ConditionalOnProperty(name = "spring.cloud.consul.config.watch.enabled", matchIfMissing = true)
|
||||
public TaskScheduler configWatchTaskScheduler() {
|
||||
return new ThreadPoolTaskScheduler();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,6 +35,7 @@ import static org.mockito.ArgumentMatchers.nullable;
|
||||
import static org.mockito.Matchers.any;
|
||||
import static org.mockito.Matchers.anyString;
|
||||
import static org.mockito.Matchers.eq;
|
||||
import static org.mockito.Mockito.atLeastOnce;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.times;
|
||||
@@ -60,7 +61,7 @@ public class ConfigWatchTests {
|
||||
|
||||
setupWatch(eventPublisher, new GetValue(), "/app/", "2ee647bd-bd69-4118-9f34-b9a6e9e60746");
|
||||
|
||||
verify(eventPublisher, times(1)).publishEvent(any(RefreshEvent.class));
|
||||
verify(eventPublisher, atLeastOnce()).publishEvent(any(RefreshEvent.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
Reference in New Issue
Block a user