@@ -41,7 +41,7 @@ public class ZookeeperProperties {
|
||||
/**
|
||||
* @param maxRetries max number of times to retry
|
||||
*/
|
||||
private Integer maxRetries = 50;
|
||||
private Integer maxRetries = 10;
|
||||
|
||||
/**
|
||||
* @param maxSleepMs max time in ms to sleep on each retry
|
||||
|
||||
@@ -75,13 +75,15 @@ public class ZookeeperDiscoveryClientConfiguration {
|
||||
protected static class ZookeeperDiscoveryHealthConfig {
|
||||
@Autowired
|
||||
private ZookeeperServiceDiscovery serviceDiscovery;
|
||||
@Autowired
|
||||
private ZookeeperDiscoveryProperties zookeeperDiscoveryProperties;
|
||||
@Autowired(required = false)
|
||||
private ZookeeperDependencies zookeeperDependencies;
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public ZookeeperDiscoveryHealthIndicator zookeeperDiscoveryHealthIndicator() {
|
||||
return new ZookeeperDiscoveryHealthIndicator(serviceDiscovery, zookeeperDependencies);
|
||||
return new ZookeeperDiscoveryHealthIndicator(serviceDiscovery, zookeeperDependencies, zookeeperDiscoveryProperties);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -16,15 +16,15 @@
|
||||
|
||||
package org.springframework.cloud.zookeeper.discovery;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.curator.x.discovery.ServiceInstance;
|
||||
import org.springframework.boot.actuate.health.AbstractHealthIndicator;
|
||||
import org.springframework.boot.actuate.health.Health;
|
||||
import org.springframework.cloud.zookeeper.discovery.dependency.ZookeeperDependencies;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
@@ -34,24 +34,20 @@ public class ZookeeperDiscoveryHealthIndicator extends AbstractHealthIndicator {
|
||||
|
||||
private ZookeeperServiceDiscovery serviceDiscovery;
|
||||
private ZookeeperDependencies zookeeperDependencies;
|
||||
private ZookeeperDiscoveryProperties zookeeperDiscoveryProperties;
|
||||
|
||||
public ZookeeperDiscoveryHealthIndicator(ZookeeperServiceDiscovery serviceDiscovery, ZookeeperDependencies zookeeperDependencies) {
|
||||
public ZookeeperDiscoveryHealthIndicator(ZookeeperServiceDiscovery serviceDiscovery, ZookeeperDependencies zookeeperDependencies,
|
||||
ZookeeperDiscoveryProperties zookeeperDiscoveryProperties) {
|
||||
this.serviceDiscovery = serviceDiscovery;
|
||||
this.zookeeperDependencies = zookeeperDependencies;
|
||||
this.zookeeperDiscoveryProperties = zookeeperDiscoveryProperties;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doHealthCheck(Health.Builder builder) throws Exception {
|
||||
try {
|
||||
Collection<String> names = getNamesToQuery();
|
||||
ArrayList<ServiceInstance<ZookeeperInstance>> allInstances = new ArrayList<>();
|
||||
for (String name : names) {
|
||||
Collection<ServiceInstance<ZookeeperInstance>> instances = this.serviceDiscovery
|
||||
.getServiceDiscovery().queryForInstances(name);
|
||||
for (ServiceInstance<ZookeeperInstance> instance : instances) {
|
||||
allInstances.add(instance);
|
||||
}
|
||||
}
|
||||
Iterable<ServiceInstance<ZookeeperInstance>> allInstances = new ZookeeperServiceInstances(serviceDiscovery,
|
||||
zookeeperDependencies, zookeeperDiscoveryProperties);
|
||||
builder.up().withDetail("services", allInstances);
|
||||
}
|
||||
catch (Exception e) {
|
||||
@@ -59,11 +55,4 @@ public class ZookeeperDiscoveryHealthIndicator extends AbstractHealthIndicator {
|
||||
builder.down(e);
|
||||
}
|
||||
}
|
||||
|
||||
private Collection<String> getNamesToQuery() throws Exception {
|
||||
if (this.zookeeperDependencies == null) {
|
||||
return this.serviceDiscovery.getServiceDiscovery().queryForNames();
|
||||
}
|
||||
return this.zookeeperDependencies.getDependencyNames();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,8 +16,6 @@
|
||||
|
||||
package org.springframework.cloud.zookeeper.discovery;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
|
||||
import com.netflix.client.config.IClientConfig;
|
||||
import com.netflix.config.ConfigurationManager;
|
||||
import com.netflix.config.DynamicPropertyFactory;
|
||||
@@ -26,16 +24,20 @@ import com.netflix.loadbalancer.ILoadBalancer;
|
||||
import com.netflix.loadbalancer.IPing;
|
||||
import com.netflix.loadbalancer.PingUrl;
|
||||
import com.netflix.loadbalancer.ServerList;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.cloud.zookeeper.discovery.dependency.ConditionalOnDependenciesNotPassed;
|
||||
import org.springframework.cloud.zookeeper.discovery.dependency.ConditionalOnDependenciesPassed;
|
||||
import org.springframework.cloud.zookeeper.discovery.dependency.DependenciesBasedLoadBalancer;
|
||||
import org.springframework.cloud.zookeeper.discovery.dependency.ZookeeperDependencies;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
|
||||
import static com.netflix.client.config.CommonClientConfigKey.DeploymentContextBasedVipAddresses;
|
||||
import static com.netflix.client.config.CommonClientConfigKey.EnableZoneAffinity;
|
||||
|
||||
@@ -49,6 +51,7 @@ import static com.netflix.client.config.CommonClientConfigKey.EnableZoneAffinity
|
||||
* @author Marcin Grzejszczak, 4financeIT
|
||||
*/
|
||||
@Configuration
|
||||
@Slf4j
|
||||
public class ZookeeperRibbonClientConfiguration {
|
||||
protected static final String VALUE_NOT_SET = "__not__set__";
|
||||
protected static final String DEFAULT_NAMESPACE = "ribbon";
|
||||
@@ -68,6 +71,7 @@ public class ZookeeperRibbonClientConfiguration {
|
||||
public ServerList<?> ribbonServerListFromDependencies(IClientConfig config, ZookeeperDependencies zookeeperDependencies) {
|
||||
ZookeeperServerList serverList = new ZookeeperServerList(this.serviceDiscovery.getServiceDiscovery());
|
||||
serverList.initFromDependencies(config, zookeeperDependencies);
|
||||
log.debug("Server list for Ribbon's dependencies based load balancing is [{}]", serverList);
|
||||
return serverList;
|
||||
}
|
||||
|
||||
@@ -89,9 +93,11 @@ public class ZookeeperRibbonClientConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
@ConditionalOnDependenciesNotPassed
|
||||
public ServerList<?> ribbonServerList(IClientConfig config) {
|
||||
ZookeeperServerList serverList = new ZookeeperServerList(this.serviceDiscovery.getServiceDiscovery());
|
||||
serverList.initWithNiwsConfig(config);
|
||||
log.debug("Server list for Ribbon's non-dependency based load balancing is [{}]", serverList);
|
||||
return serverList;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,118 @@
|
||||
package org.springframework.cloud.zookeeper.discovery;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.curator.x.discovery.ServiceInstance;
|
||||
import org.springframework.cloud.zookeeper.discovery.dependency.ZookeeperDependencies;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
@Slf4j
|
||||
public class ZookeeperServiceInstances implements Iterable<ServiceInstance<ZookeeperInstance>> {
|
||||
|
||||
private final ZookeeperServiceDiscovery serviceDiscovery;
|
||||
private final ZookeeperDependencies zookeeperDependencies;
|
||||
private final ZookeeperDiscoveryProperties zookeeperDiscoveryProperties;
|
||||
private final List<ServiceInstance<ZookeeperInstance>> allInstances;
|
||||
|
||||
public ZookeeperServiceInstances(ZookeeperServiceDiscovery serviceDiscovery, ZookeeperDependencies zookeeperDependencies,
|
||||
ZookeeperDiscoveryProperties zookeeperDiscoveryProperties) {
|
||||
this.serviceDiscovery = serviceDiscovery;
|
||||
this.zookeeperDependencies = zookeeperDependencies;
|
||||
this.zookeeperDiscoveryProperties = zookeeperDiscoveryProperties;
|
||||
this.allInstances = getZookeeperInstances();
|
||||
}
|
||||
|
||||
private List<ServiceInstance<ZookeeperInstance>> getZookeeperInstances() {
|
||||
ArrayList<ServiceInstance<ZookeeperInstance>> allInstances = new ArrayList<>();
|
||||
try {
|
||||
Collection<String> names = getNamesToQuery();
|
||||
for (String name : names) {
|
||||
allInstances.addAll(nestedInstances(allInstances, name));
|
||||
}
|
||||
return allInstances;
|
||||
}
|
||||
catch (Exception e) {
|
||||
log.debug("Exception occurred while trying to build the list of instances", e);
|
||||
return allInstances;
|
||||
}
|
||||
}
|
||||
|
||||
private List<ServiceInstance<ZookeeperInstance>> nestedInstances(
|
||||
List<ServiceInstance<ZookeeperInstance>> accumulator, String name) throws Exception {
|
||||
String parentPath = prepareQueryName(name);
|
||||
Collection<ServiceInstance<ZookeeperInstance>> childrenInstances = tryToGetInstances(parentPath);
|
||||
if (childrenInstances != null) {
|
||||
return convertCollectionToList(childrenInstances);
|
||||
}
|
||||
try {
|
||||
List<String> children = this.serviceDiscovery.getCurator().getChildren().forPath(parentPath);
|
||||
return iterateOverChildren(accumulator, parentPath, children);
|
||||
} catch (Exception e) {
|
||||
log.trace("Exception occurred while trying to retrieve children of [" + parentPath + "]", e);
|
||||
return injectZookeeperServiceInstances(accumulator, parentPath);
|
||||
}
|
||||
}
|
||||
|
||||
private String prepareQueryName(String name) {
|
||||
if (name.startsWith(this.zookeeperDiscoveryProperties.getRoot())) {
|
||||
return name;
|
||||
}
|
||||
String queryName = this.zookeeperDiscoveryProperties.getRoot();
|
||||
if (!queryName.endsWith("/")) {
|
||||
queryName = queryName + "/";
|
||||
}
|
||||
return queryName + name;
|
||||
}
|
||||
|
||||
private Collection<ServiceInstance<ZookeeperInstance>> tryToGetInstances(String path) {
|
||||
try {
|
||||
return this.serviceDiscovery
|
||||
.getServiceDiscovery().queryForInstances(path.substring(
|
||||
this.zookeeperDiscoveryProperties.getRoot().length()));
|
||||
} catch (Exception e) {
|
||||
log.trace("Exception occurred while trying to retrieve instances of [" + path + "]", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private List<ServiceInstance<ZookeeperInstance>> injectZookeeperServiceInstances(
|
||||
List<ServiceInstance<ZookeeperInstance>> accumulator, String name) throws Exception {
|
||||
Collection<ServiceInstance<ZookeeperInstance>> instances = this.serviceDiscovery
|
||||
.getServiceDiscovery().queryForInstances(name);
|
||||
accumulator.addAll(convertCollectionToList(instances));
|
||||
return accumulator;
|
||||
}
|
||||
|
||||
private List<ServiceInstance<ZookeeperInstance>> convertCollectionToList(
|
||||
Collection<ServiceInstance<ZookeeperInstance>> instances) {
|
||||
List<ServiceInstance<ZookeeperInstance>> serviceInstances = new ArrayList<>();
|
||||
for (ServiceInstance<ZookeeperInstance> instance : instances) {
|
||||
serviceInstances.add(instance);
|
||||
}
|
||||
return serviceInstances;
|
||||
}
|
||||
|
||||
private List<ServiceInstance<ZookeeperInstance>> iterateOverChildren(List<ServiceInstance<ZookeeperInstance>> accumulator,
|
||||
String parentPath, List<String> children) throws Exception {
|
||||
List<ServiceInstance<ZookeeperInstance>> lists = new ArrayList<>();
|
||||
for (String child : children) {
|
||||
lists.addAll(nestedInstances(accumulator, parentPath + "/" + child));
|
||||
}
|
||||
return lists;
|
||||
}
|
||||
|
||||
private Collection<String> getNamesToQuery() throws Exception {
|
||||
if (this.zookeeperDependencies == null) {
|
||||
return this.serviceDiscovery.getServiceDiscovery().queryForNames();
|
||||
}
|
||||
return this.zookeeperDependencies.getDependencyNames();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<ServiceInstance<ZookeeperInstance>> iterator() {
|
||||
return allInstances.iterator();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright 2013-2015 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.zookeeper.discovery.dependency;
|
||||
|
||||
import org.springframework.context.annotation.Conditional;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Annotation to turn off a feature if Zookeeper dependencies have NOT been passed
|
||||
*
|
||||
* @author Marcin Grzejszczak
|
||||
*/
|
||||
@Target({ElementType.TYPE, ElementType.METHOD})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Conditional(DependenciesNotPassedCondition.class)
|
||||
public @interface ConditionalOnDependenciesNotPassed {
|
||||
}
|
||||
@@ -55,6 +55,7 @@ public class DependenciesBasedLoadBalancer extends DynamicServerListLoadBalancer
|
||||
public Server chooseServer(Object key) {
|
||||
String keyAsString = (String) key;
|
||||
ZookeeperDependency dependency = this.zookeeperDependencies.getDependencyForAlias(keyAsString);
|
||||
log.debug("Current dependencies are [{}]", this.zookeeperDependencies);
|
||||
if (dependency == null) {
|
||||
log.debug("No dependency found for alias [{}] - will use the default rule which is [{}]", keyAsString, this.rule);
|
||||
return this.rule.choose(key);
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright 2013-2015 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.cloud.zookeeper.discovery.dependency;
|
||||
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionOutcome;
|
||||
import org.springframework.context.annotation.ConditionContext;
|
||||
import org.springframework.core.type.AnnotatedTypeMetadata;
|
||||
|
||||
/**
|
||||
* Inverse of the {@link ConditionalOnDependenciesPassed} condition. Also checks if switch for zookeeper dependencies
|
||||
* was turned on
|
||||
*
|
||||
* @author Marcin Grzejszczak
|
||||
*/
|
||||
public class DependenciesNotPassedCondition extends DependenciesPassedCondition {
|
||||
|
||||
@Override
|
||||
public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {
|
||||
ConditionOutcome propertiesSet = super.getMatchOutcome(context, metadata);
|
||||
if (propertiesSet.isMatch()) {
|
||||
return ConditionOutcome.inverse(propertiesSet);
|
||||
}
|
||||
Boolean dependenciesEnabled = context.getEnvironment()
|
||||
.getProperty("spring.cloud.zookeeper.dependencies.enabled", Boolean.class, false);
|
||||
if (dependenciesEnabled) {
|
||||
return ConditionOutcome.noMatch("Dependencies are defined in configuration and switch is turned on");
|
||||
}
|
||||
return ConditionOutcome.match("Dependencies are not defined in configuration and switch is turned off");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -10,13 +10,19 @@ import org.apache.curator.x.discovery.UriSpec
|
||||
class CustomZookeeperServiceDiscovery extends ZookeeperServiceDiscovery {
|
||||
|
||||
private final String applicationName
|
||||
private final String basePath
|
||||
|
||||
CustomZookeeperServiceDiscovery(String applicationName, CuratorFramework curator) {
|
||||
CustomZookeeperServiceDiscovery(String applicationName, String basePath, CuratorFramework curator) {
|
||||
super(curator, null, null)
|
||||
this.applicationName = applicationName
|
||||
this.basePath = basePath
|
||||
build()
|
||||
}
|
||||
|
||||
CustomZookeeperServiceDiscovery(String applicationName, CuratorFramework curator) {
|
||||
this(applicationName, '/', curator)
|
||||
}
|
||||
|
||||
@Override
|
||||
void build() {
|
||||
setPort(10)
|
||||
@@ -28,7 +34,7 @@ class CustomZookeeperServiceDiscovery extends ZookeeperServiceDiscovery {
|
||||
getServiceInstanceRef().set(instance)
|
||||
def discovery = ServiceDiscoveryBuilder
|
||||
.builder(Void)
|
||||
.basePath('/')
|
||||
.basePath(basePath)
|
||||
.client(getCurator())
|
||||
.thisInstance(instance)
|
||||
.build()
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
package org.springframework.cloud.zookeeper.discovery
|
||||
|
||||
import groovy.json.JsonSlurper
|
||||
import org.apache.curator.framework.CuratorFramework
|
||||
import org.springframework.beans.factory.annotation.Autowired
|
||||
import org.springframework.beans.factory.annotation.Value
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration
|
||||
import org.springframework.boot.test.SpringApplicationContextLoader
|
||||
import org.springframework.boot.test.WebIntegrationTest
|
||||
import org.springframework.cloud.client.discovery.EnableDiscoveryClient
|
||||
import org.springframework.cloud.client.loadbalancer.LoadBalanced
|
||||
import org.springframework.cloud.zookeeper.discovery.test.CommonTestConfig
|
||||
import org.springframework.cloud.zookeeper.discovery.test.TestRibbonClient
|
||||
import org.springframework.context.annotation.Bean
|
||||
import org.springframework.context.annotation.Configuration
|
||||
import org.springframework.context.annotation.Import
|
||||
import org.springframework.context.annotation.Profile
|
||||
import org.springframework.test.context.ActiveProfiles
|
||||
import org.springframework.test.context.ContextConfiguration
|
||||
import org.springframework.web.client.RestTemplate
|
||||
import spock.lang.Issue
|
||||
import spock.lang.Specification
|
||||
|
||||
import javax.annotation.PostConstruct
|
||||
import javax.annotation.PreDestroy
|
||||
|
||||
@ContextConfiguration(classes = Config, loader = SpringApplicationContextLoader)
|
||||
@ActiveProfiles('nestedstructure')
|
||||
@WebIntegrationTest(randomPort = true)
|
||||
class ZookeeperDiscoveryHealthIndicatorWithNestedStructureISpec extends Specification implements PollingUtils {
|
||||
|
||||
@Autowired TestRibbonClient testRibbonClient
|
||||
|
||||
@Issue("#54 - ZookeeperDiscoveryHealthIndicator fails on nested structure")
|
||||
def 'should return a response that app is in a healthy state when nested folders in zookeeper are present'() {
|
||||
when:
|
||||
String response = testRibbonClient.callService('me', 'health')
|
||||
then:
|
||||
twoServicesArePresentedInHealthEndpoint(response)
|
||||
}
|
||||
|
||||
private boolean twoServicesArePresentedInHealthEndpoint(String response) {
|
||||
def services = new JsonSlurper().parseText(response).zookeeperDiscovery.services
|
||||
assert ['me', '/a/b/c/d/anotherservice'].every { expectedServiceName ->
|
||||
services.any { service ->
|
||||
expectedServiceName == service.name
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableAutoConfiguration
|
||||
@EnableDiscoveryClient
|
||||
@Import(CommonTestConfig)
|
||||
@Profile('nestedstructure')
|
||||
static class Config {
|
||||
|
||||
@Autowired CuratorFramework curatorFramework
|
||||
CustomZookeeperServiceDiscovery customZookeeperServiceDiscovery
|
||||
|
||||
@PostConstruct
|
||||
void registerNestedDependency() {
|
||||
customZookeeperServiceDiscovery = new CustomZookeeperServiceDiscovery("/a/b/c/d/anotherservice",
|
||||
'/services', curatorFramework).build()
|
||||
}
|
||||
|
||||
@PreDestroy
|
||||
void unregisterServiceDiscovery() {
|
||||
customZookeeperServiceDiscovery?.close()
|
||||
}
|
||||
|
||||
@Bean
|
||||
TestRibbonClient testRibbonClient(@LoadBalanced RestTemplate restTemplate,
|
||||
@Value('${spring.application.name}') String springAppName) {
|
||||
return new TestRibbonClient(restTemplate, springAppName)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
spring.application.name: me
|
||||
Reference in New Issue
Block a user