Merge pull request #48 from marcingrzejszczak/issues/45-broken-load-balancer

* issues/45-broken-load-balancer:
  Dependency based load balancer fixes.
This commit is contained in:
Spencer Gibb
2015-11-04 15:23:41 -07:00
14 changed files with 348 additions and 160 deletions

View File

@@ -56,13 +56,13 @@ public class ZookeeperDiscoveryClientConfiguration {
}
@Bean
public ZookeeperLifecycle zookeeperLifecycle() {
return new ZookeeperLifecycle(zookeeperDiscoveryProperties(), zookeeperServiceDiscovery());
public ZookeeperLifecycle zookeeperLifecycle(ZookeeperServiceDiscovery zookeeperServiceDiscovery) {
return new ZookeeperLifecycle(zookeeperDiscoveryProperties(), zookeeperServiceDiscovery);
}
@Bean
public ZookeeperDiscoveryClient zookeeperDiscoveryClient() {
return new ZookeeperDiscoveryClient(zookeeperServiceDiscovery(), zookeeperDependencies);
public ZookeeperDiscoveryClient zookeeperDiscoveryClient(ZookeeperServiceDiscovery zookeeperServiceDiscovery) {
return new ZookeeperDiscoveryClient(zookeeperServiceDiscovery, zookeeperDependencies);
}
@Bean

View File

@@ -16,11 +16,16 @@
package org.springframework.cloud.zookeeper.discovery;
import static com.netflix.client.config.CommonClientConfigKey.DeploymentContextBasedVipAddresses;
import static com.netflix.client.config.CommonClientConfigKey.EnableZoneAffinity;
import javax.annotation.PostConstruct;
import com.netflix.client.config.IClientConfig;
import com.netflix.config.ConfigurationManager;
import com.netflix.config.DynamicPropertyFactory;
import com.netflix.config.DynamicStringProperty;
import com.netflix.loadbalancer.ILoadBalancer;
import com.netflix.loadbalancer.IPing;
import com.netflix.loadbalancer.PingUrl;
import com.netflix.loadbalancer.ServerList;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
@@ -31,12 +36,8 @@ import org.springframework.cloud.zookeeper.discovery.dependency.ZookeeperDepende
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.netflix.client.config.IClientConfig;
import com.netflix.config.ConfigurationManager;
import com.netflix.config.DynamicPropertyFactory;
import com.netflix.config.DynamicStringProperty;
import com.netflix.loadbalancer.ILoadBalancer;
import com.netflix.loadbalancer.ServerList;
import static com.netflix.client.config.CommonClientConfigKey.DeploymentContextBasedVipAddresses;
import static com.netflix.client.config.CommonClientConfigKey.EnableZoneAffinity;
/**
* Preprocessor that configures defaults for zookeeper-discovered ribbon clients. Such as:
@@ -74,8 +75,16 @@ public class ZookeeperRibbonClientConfiguration {
@ConditionalOnMissingBean
@ConditionalOnDependenciesPassed
@ConditionalOnProperty(value = "spring.cloud.zookeeper.dependencies.ribbon.loadbalancer", matchIfMissing = true)
public ILoadBalancer dependenciesBasedLoadBalancer(ZookeeperDependencies zookeeperDependencies, ServerList<?> serverList) {
return new DependenciesBasedLoadBalancer(zookeeperDependencies, serverList);
public ILoadBalancer dependenciesBasedLoadBalancer(ZookeeperDependencies zookeeperDependencies,
ServerList<?> serverList, IClientConfig config, IPing iPing) {
return new DependenciesBasedLoadBalancer(zookeeperDependencies, serverList, config, iPing);
}
@Bean
@ConditionalOnMissingBean
@ConditionalOnDependenciesPassed
public IPing healthCheckingRule(ZookeeperDependencies zookeeperDependencies) {
return new PingUrl(false, zookeeperDependencies.getDefaultHealthEndpoint());
}
@Bean

View File

@@ -19,7 +19,9 @@ package org.springframework.cloud.zookeeper.discovery.dependency;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import com.netflix.loadbalancer.BaseLoadBalancer;
import com.netflix.client.config.IClientConfig;
import com.netflix.loadbalancer.DynamicServerListLoadBalancer;
import com.netflix.loadbalancer.IPing;
import com.netflix.loadbalancer.IRule;
import com.netflix.loadbalancer.RandomRule;
import com.netflix.loadbalancer.RoundRobinRule;
@@ -35,15 +37,18 @@ import lombok.extern.slf4j.Slf4j;
* @author Marcin Grzejszczak, 4financeIT
*/
@Slf4j
public class DependenciesBasedLoadBalancer extends BaseLoadBalancer {
public class DependenciesBasedLoadBalancer extends DynamicServerListLoadBalancer {
private final Map<String, IRule> ruleCache = new ConcurrentHashMap<>();
private final ZookeeperDependencies zookeeperDependencies;
public DependenciesBasedLoadBalancer(ZookeeperDependencies zookeeperDependencies, ServerList<?> serverList) {
public DependenciesBasedLoadBalancer(ZookeeperDependencies zookeeperDependencies, ServerList<?> serverList, IClientConfig config, IPing iPing) {
super(config);
this.zookeeperDependencies = zookeeperDependencies;
setServersList(serverList.getInitialListOfServers());
setPing(iPing);
setServerListImpl(serverList);
}
@Override
@@ -56,6 +61,7 @@ public class DependenciesBasedLoadBalancer extends BaseLoadBalancer {
};
cacheEntryIfMissing(keyAsString, dependency);
log.debug("Will try to retrieve dependency for key [{}]. Current cache contents [{}]", keyAsString, this.ruleCache);
updateListOfServers();
return this.ruleCache.get(keyAsString).choose(key);
}

View File

@@ -23,11 +23,12 @@ import java.util.Map;
import javax.annotation.PostConstruct;
import lombok.Data;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.util.StringUtils;
import lombok.Data;
/**
* Representation of this service's dependencies in Zookeeper
*
@@ -48,6 +49,12 @@ public class ZookeeperDependencies {
*/
private Map<String, ZookeeperDependency> dependencies = new LinkedHashMap<>();
/**
* Default health endpoint that will be checked to verify that a dependency is alive
*/
@Value("${spring.cloud.zookeeper.dependencies.ribbon.loadbalancer.defaulthealthendpoint:/health}")
private String defaultHealthEndpoint;
@PostConstruct
public void init() {
if (StringUtils.hasText(prefix) && !prefix.endsWith("/")) {

View File

@@ -0,0 +1,43 @@
package org.springframework.cloud.zookeeper.discovery
import javax.annotation.PreDestroy
import org.apache.curator.framework.CuratorFramework
import org.apache.curator.x.discovery.ServiceDiscoveryBuilder
import org.apache.curator.x.discovery.ServiceInstance
import org.apache.curator.x.discovery.UriSpec
class CustomZookeeperServiceDiscovery extends ZookeeperServiceDiscovery {
private final String applicationName
CustomZookeeperServiceDiscovery(String applicationName, CuratorFramework curator) {
super(curator, null, null)
this.applicationName = applicationName
build()
}
@Override
void build() {
setPort(10)
def instance = ServiceInstance.builder().uriSpec(new UriSpec("{scheme}://{address}:{port}/"))
.address('anyUrl')
.port(10)
.name(applicationName)
.build()
getServiceInstanceRef().set(instance)
def discovery = ServiceDiscoveryBuilder
.builder(Void)
.basePath('/')
.client(getCurator())
.thisInstance(instance)
.build()
getServiceDiscoveryRef().set(discovery)
discovery.start()
}
@PreDestroy
void close() {
getServiceDiscoveryRef().get().close()
}
}

View File

@@ -0,0 +1,104 @@
package org.springframework.cloud.zookeeper.discovery.dependency
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.actuate.autoconfigure.EndpointMBeanExportAutoConfiguration
import org.springframework.boot.autoconfigure.EnableAutoConfiguration
import org.springframework.boot.context.embedded.EmbeddedServletContainerInitializedEvent
import org.springframework.cloud.client.discovery.EnableDiscoveryClient
import org.springframework.cloud.client.loadbalancer.LoadBalanced
import org.springframework.cloud.netflix.feign.EnableFeignClients
import org.springframework.cloud.netflix.feign.FeignClient
import org.springframework.cloud.zookeeper.discovery.ZookeeperServiceDiscovery
import org.springframework.cloud.zookeeper.discovery.test.CommonTestConfig
import org.springframework.cloud.zookeeper.discovery.test.TestRibbonClient
import org.springframework.context.ApplicationListener
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.context.annotation.Import
import org.springframework.web.bind.annotation.RequestHeader
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RequestMethod
import org.springframework.web.bind.annotation.RestController
import org.springframework.web.client.RestTemplate
@Configuration
@EnableAutoConfiguration(exclude = [EndpointMBeanExportAutoConfiguration])
@Import(CommonTestConfig)
@EnableDiscoveryClient
@EnableFeignClients(basePackageClasses = [AliasUsingFeignClient, IdUsingFeignClient])
class DependencyConfig {
@Autowired ZookeeperServiceDiscovery zookeeperServiceDiscovery
@Bean
TestRibbonClient testRibbonClient(@LoadBalanced RestTemplate restTemplate) {
return new TestRibbonClient(restTemplate)
}
@Bean
PingController pingController() {
return new PingController(portListener())
}
@Bean
PortListener portListener() {
return new PortListener()
}
}
class PortListener implements ApplicationListener<EmbeddedServletContainerInitializedEvent> {
private int port
@Override
public void onApplicationEvent(EmbeddedServletContainerInitializedEvent event) {
this.port = event.getEmbeddedServletContainer().getPort()
}
public int getPort() {
return port
}
}
@FeignClient("someAlias")
interface AliasUsingFeignClient {
@RequestMapping(method = RequestMethod.GET, value = "/beans")
String getBeans()
@RequestMapping(method = RequestMethod.GET, value = "/checkHeaders")
String checkHeaders()
}
@FeignClient("nameWithoutAlias")
interface IdUsingFeignClient {
@RequestMapping(method = RequestMethod.GET, value = "/beans")
String getBeans()
}
@RestController
class PingController {
private final PortListener portListener
PingController(PortListener portListener) {
this.portListener = portListener
}
@RequestMapping('/ping') String ping() {
return 'pong'
}
@RequestMapping('/port') Integer port() {
return portListener.port
}
@RequestMapping('/checkHeaders') String checkHeaders(@RequestHeader('Content-Type') String contentType,
@RequestHeader('header1') Collection<String> header1,
@RequestHeader('header2') Collection<String> header2) {
assert contentType == 'application/vnd.newsletter.v1+json'
assert header1 == ['value1'] as Set
assert header2 == ['value2'] as Set
return 'ok'
}
}

View File

@@ -14,6 +14,8 @@
* limitations under the License.
*/
package org.springframework.cloud.zookeeper.discovery.dependency
import com.netflix.loadbalancer.IPing
import com.netflix.loadbalancer.NoOpPing
import org.apache.curator.framework.CuratorFramework
import org.apache.curator.test.TestingServer
import org.springframework.beans.factory.annotation.Autowired
@@ -34,6 +36,7 @@ import org.springframework.test.context.ContextConfiguration
import org.springframework.util.SocketUtils
import spock.lang.Specification
import spock.util.concurrent.PollingConditions
import spock.util.environment.RestoreSystemProperties
@ContextConfiguration(classes = Config, loader = SpringApplicationContextLoader)
@ActiveProfiles('loadbalancerclient')
@@ -48,7 +51,10 @@ class StickyRuleISpec extends Specification implements PollingUtils {
conditions = new PollingConditions()
}
@RestoreSystemProperties
def 'should use sticky load balancing strategy taken from Zookeeper dependencies'() {
given:
System.setProperty('spring.cloud.zookeeper.dependencies.ribbon.loadbalancer.checkping', 'false')
expect:
thereAreTwoRegisteredServices()
URI uri = getUriForAlias()
@@ -91,5 +97,8 @@ class StickyRuleISpec extends Specification implements PollingUtils {
return new TestServiceRegistrar(SocketUtils.findAvailableTcpPort(), curatorFramework)
}
@Bean IPing noOpPing() {
return new NoOpPing()
}
}
}

View File

@@ -5,7 +5,7 @@
* 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
* 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,
@@ -14,30 +14,20 @@
* limitations under the License.
*/
package org.springframework.cloud.zookeeper.discovery.dependency
import org.apache.curator.framework.CuratorFramework
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.autoconfigure.EnableAutoConfiguration
import org.springframework.boot.test.SpringApplicationContextLoader
import org.springframework.boot.test.WebIntegrationTest
import org.springframework.cloud.client.ServiceInstance
import org.springframework.cloud.client.discovery.DiscoveryClient
import org.springframework.cloud.client.discovery.EnableDiscoveryClient
import org.springframework.cloud.client.loadbalancer.LoadBalanced
import org.springframework.cloud.netflix.feign.EnableFeignClients
import org.springframework.cloud.netflix.feign.FeignClient
import org.springframework.cloud.zookeeper.discovery.test.CommonTestConfig
import org.springframework.cloud.zookeeper.discovery.test.TestRibbonClient
import org.springframework.cloud.zookeeper.discovery.PollingUtils
import org.springframework.context.annotation.Bean
import org.springframework.cloud.zookeeper.discovery.test.TestRibbonClient
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.bind.annotation.RequestHeader
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RequestMethod
import org.springframework.web.bind.annotation.RestController
import org.springframework.web.client.RestTemplate
import spock.lang.Specification
import spock.util.concurrent.PollingConditions
@@ -50,7 +40,9 @@ class ZookeeperDiscoveryWithDependenciesISpec extends Specification implements P
@Autowired DiscoveryClient discoveryClient
@Autowired AliasUsingFeignClient aliasUsingFeignClient
@Autowired IdUsingFeignClient idUsingFeignClient
@Autowired ZookeeperDependencies zookeeperDependencies
@Autowired ZookeeperDependencies zookeeperDependencies
@Autowired Config dependencyConfig
@Autowired CuratorFramework curatorFramework
PollingConditions conditions = new PollingConditions()
def 'should find an instance via path when alias is not found'() {
@@ -105,28 +97,28 @@ class ZookeeperDiscoveryWithDependenciesISpec extends Specification implements P
}
}
def 'should have path equal to alias'() {
given:
def dependency = zookeeperDependencies.getDependencyForAlias('aliasIsPath')
expect:
dependency.path == 'aliasIsPath'
}
def 'should have path equal to alias'() {
given:
def dependency = zookeeperDependencies.getDependencyForAlias('aliasIsPath')
expect:
dependency.path == 'aliasIsPath'
}
def 'should have alias equal to path'() {
given:
def dependency = zookeeperDependencies.getDependencyForPath('aliasIsPath')
expect:
dependency.path == 'aliasIsPath'
}
def 'should have alias equal to path'() {
given:
def dependency = zookeeperDependencies.getDependencyForPath('aliasIsPath')
expect:
dependency.path == 'aliasIsPath'
}
def 'should have path set via string constructor'() {
given:
def dependency = zookeeperDependencies.getDependencyForAlias('anotherAlias')
expect:
dependency.path == 'myPath'
}
def 'should have path set via string constructor'() {
given:
def dependency = zookeeperDependencies.getDependencyForAlias('anotherAlias')
expect:
dependency.path == 'myPath'
}
private boolean callingServiceAtBeansEndpointIsNotEmpty() {
private boolean callingServiceAtBeansEndpointIsNotEmpty() {
return !testRibbonClient.callService('someAlias', 'beans').empty
}
@@ -140,55 +132,9 @@ class ZookeeperDiscoveryWithDependenciesISpec extends Specification implements P
@Configuration
@EnableAutoConfiguration
@Import(CommonTestConfig)
@EnableDiscoveryClient
@EnableFeignClients(clients = [AliasUsingFeignClient, IdUsingFeignClient])
@Import(DependencyConfig)
@Profile('dependencies')
static class Config {
@Bean
TestRibbonClient testRibbonClient(@LoadBalanced RestTemplate restTemplate) {
return new TestRibbonClient(restTemplate)
}
@Bean
PingController pingController() {
return new PingController()
}
}
@FeignClient("someAlias")
public static interface AliasUsingFeignClient {
@RequestMapping(method = RequestMethod.GET, value = "/beans")
String getBeans();
@RequestMapping(method = RequestMethod.GET, value = "/checkHeaders")
String checkHeaders();
}
@FeignClient("nameWithoutAlias")
public static interface IdUsingFeignClient {
@RequestMapping(method = RequestMethod.GET, value = "/beans")
String getBeans();
}
@RestController
@Profile('dependencies')
static class PingController {
@RequestMapping('/ping') String ping() {
return 'pong'
}
@RequestMapping('/checkHeaders') String checkHeaders(@RequestHeader('Content-Type') String contentType,
@RequestHeader('header1') Collection<String> header1,
@RequestHeader('header2') Collection<String> header2) {
assert contentType == 'application/vnd.newsletter.v1+json'
assert header1 == ['value1'] as Set
assert header2 == ['value2'] as Set
return 'ok'
}
}
}

View File

@@ -0,0 +1,80 @@
/*
* 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.apache.curator.test.TestingServer
import org.springframework.boot.autoconfigure.EnableAutoConfiguration
import org.springframework.boot.builder.SpringApplicationBuilder
import org.springframework.cloud.client.discovery.EnableDiscoveryClient
import org.springframework.cloud.zookeeper.discovery.PollingUtils
import org.springframework.cloud.zookeeper.discovery.test.TestRibbonClient
import org.springframework.context.ApplicationContext
import org.springframework.context.ConfigurableApplicationContext
import org.springframework.context.annotation.Configuration
import org.springframework.context.annotation.Import
import org.springframework.util.SocketUtils
import spock.lang.Issue
import spock.lang.Specification
import spock.util.concurrent.PollingConditions
import spock.util.environment.RestoreSystemProperties
class ZookeeperDiscoveryWithDyingDependenciesISpec extends Specification implements PollingUtils {
PollingConditions conditions = new PollingConditions()
@Issue("#45")
@RestoreSystemProperties
def "should refresh a dependency in Ribbon when the dependency has de-registered and registered in Zookeeper"() {
given:
int zookeeperPort = SocketUtils.findAvailableTcpPort()
TestingServer testingServer = new TestingServer(zookeeperPort)
System.setProperty('spring.jmx.enabled', 'false')
System.setProperty('spring.cloud.zookeeper.connectString', "127.0.0.1:$zookeeperPort")
and:
ConfigurableApplicationContext serverContext = contextWithProfile('server')
ConfigurableApplicationContext clientContext = contextWithProfile('client')
and:
Integer portBeforeDying = callServiceAtPortEndpoint(clientContext)
and:
serverContext = restartContext(serverContext, 'server')
expect:
callServiceAtPortEndpoint(clientContext) != portBeforeDying
cleanup:
serverContext?.close()
clientContext?.close()
testingServer?.close()
}
private ConfigurableApplicationContext contextWithProfile(String profile) {
return new SpringApplicationBuilder(Config).profiles(profile).build().run()
}
private ConfigurableApplicationContext restartContext(ConfigurableApplicationContext configurableApplicationContext, String profile) {
configurableApplicationContext.close()
return contextWithProfile(profile)
}
private Integer callServiceAtPortEndpoint(ApplicationContext applicationContext) {
return applicationContext.getBean(TestRibbonClient).callService('testInstance', 'port', Integer)
}
@Configuration
@EnableDiscoveryClient
@EnableAutoConfiguration
@Import(DependencyConfig)
static class Config {
}
}

View File

@@ -5,7 +5,7 @@
* 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
* 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,
@@ -20,20 +20,24 @@ import org.springframework.web.client.RestTemplate
class TestRibbonClient extends TestServiceRestClient {
private final String thisAppName
private final String thisAppName
TestRibbonClient(RestTemplate restTemplate) {
super(restTemplate)
this.thisAppName = 'someName'
}
TestRibbonClient(RestTemplate restTemplate) {
super(restTemplate)
this.thisAppName = 'someName'
}
TestRibbonClient(RestTemplate restTemplate, String thisAppName) {
super(restTemplate)
this.thisAppName = thisAppName
}
TestRibbonClient(RestTemplate restTemplate, String thisAppName) {
super(restTemplate)
this.thisAppName = thisAppName
}
String thisHealthCheck() {
return restTemplate.getForObject("http://$thisAppName/health", String)
}
String thisHealthCheck() {
return restTemplate.getForObject("http://$thisAppName/health", String)
}
Integer thisPort() {
return restTemplate.getForObject("http://$thisAppName/port", Integer)
}
}

View File

@@ -27,8 +27,12 @@ class TestServiceRestClient {
this.restTemplate = restTemplate
}
public <T> T callService(String alias, String endpoint, Class<T> clazz) {
return restTemplate.getForObject("http://$alias/$endpoint", clazz)
}
String callService(String alias, String endpoint) {
return restTemplate.getForObject("http://$alias/$endpoint", String)
return callService(alias, endpoint, String)
}
String callOnUrl(String url, String endpoint) {

View File

@@ -5,7 +5,7 @@
* 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
* 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,
@@ -14,17 +14,16 @@
* limitations under the License.
*/
package org.springframework.cloud.zookeeper.discovery.watcher
import org.apache.curator.framework.CuratorFramework
import org.apache.curator.framework.CuratorFrameworkFactory
import org.apache.curator.retry.ExponentialBackoffRetry
import org.apache.curator.test.TestingServer
import org.apache.curator.x.discovery.ServiceCache
import org.apache.curator.x.discovery.ServiceDiscoveryBuilder
import org.apache.curator.x.discovery.ServiceInstance
import org.apache.curator.x.discovery.UriSpec
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.autoconfigure.EnableAutoConfiguration
import org.springframework.boot.test.SpringApplicationContextLoader
import org.springframework.cloud.zookeeper.discovery.CustomZookeeperServiceDiscovery
import org.springframework.cloud.zookeeper.discovery.PollingUtils
import org.springframework.cloud.zookeeper.discovery.ZookeeperServiceDiscovery
import org.springframework.cloud.zookeeper.discovery.watcher.presence.DependencyPresenceOnStartupVerifier
@@ -39,15 +38,13 @@ import org.springframework.util.SocketUtils
import spock.lang.Specification
import spock.util.concurrent.PollingConditions
import javax.annotation.PreDestroy
@ContextConfiguration(classes = Config, loader = SpringApplicationContextLoader)
@ActiveProfiles('watcher')
class DefaultDependencyWatcherSpringISpec extends Specification implements PollingUtils {
@Autowired AssertableDependencyPresenceOnStartupVerifier dependencyPresenceOnStartupVerifier
@Autowired AssertableDependencyWatcherListener dependencyWatcherListener
@Autowired ZookeeperServiceDiscovery serviceDiscovery
@Autowired ZookeeperServiceDiscovery serviceDiscovery
PollingConditions conditions
def setup() {
@@ -61,7 +58,7 @@ class DefaultDependencyWatcherSpringISpec extends Specification implements Polli
def 'should verify that dependency watcher listener is successfully registered and operational'() {
when:
serviceDiscovery.serviceDiscovery.unregisterService(serviceDiscovery.serviceInstance)
serviceDiscovery.serviceDiscovery.unregisterService(serviceDiscovery.serviceInstance)
then:
conditions.eventually willPass {
@@ -84,10 +81,10 @@ class DefaultDependencyWatcherSpringISpec extends Specification implements Polli
return new TestingServer(SocketUtils.findAvailableTcpPort())
}
@Bean
ZookeeperServiceDiscovery zookeeperServiceDiscovery() {
return new MyZookeeperServiceDiscovery(curatorFramework())
}
@Bean
ZookeeperServiceDiscovery zookeeperServiceDiscovery() {
return new MyZookeeperServiceDiscovery(curatorFramework())
}
@Bean(initMethod = 'start', destroyMethod = 'close')
CuratorFramework curatorFramework() {
@@ -106,40 +103,11 @@ class DefaultDependencyWatcherSpringISpec extends Specification implements Polli
}
static class MyZookeeperServiceDiscovery extends ZookeeperServiceDiscovery {
MyZookeeperServiceDiscovery(CuratorFramework curator) {
super(curator, null, null)
build()
}
@Override
void build() {
setPort(10)
def instance = ServiceInstance.builder().uriSpec(new UriSpec("{scheme}://{address}:{port}/"))
.address('anyUrl')
.port(10)
.name('testInstance')
.build()
getServiceInstanceRef().set(instance)
def discovery = ServiceDiscoveryBuilder
.builder(Void)
.basePath('/')
.client(getCurator())
.thisInstance(instance)
.build()
getServiceDiscoveryRef().set(discovery)
discovery.start()
}
@PreDestroy
void close() {
getServiceDiscoveryRef().get().close()
}
}
static class MyZookeeperServiceDiscovery extends CustomZookeeperServiceDiscovery {
MyZookeeperServiceDiscovery(CuratorFramework curator) {
super('testInstance', curator)
}
}
static class AssertableDependencyWatcherListener implements DependencyWatcherListener {

View File

@@ -0,0 +1,6 @@
server.port: 0
spring.application.name: client
spring.cloud.zookeeper:
dependencies:
testInstance:
path: /server

View File

@@ -0,0 +1,2 @@
server.port: 0
spring.application.name: server