Extract ribbon client factory into ApplicationContext
Using Spring as an object factory to control the lifecycle of ribbon clients.
So far we have a single, parameterized configuration class for all clients, but
can be extended to allow user to supply additional configuration. The current
model for user extensions is
@EnableRibbonClient(@RibbonClient(name = "foo", configuration = FooConfiguration.class))
public class MainConfiguration {
...
}
So in this example, MainConfiguration is part of the "main" application context
and FooConfiguration is used to create the Ribbon client and load balancer for
the "foo" service.
This commit is contained in:
@@ -8,8 +8,11 @@ import static com.netflix.config.ConfigurationManager.ENV_CONFIG_NAME;
|
||||
import static com.netflix.config.ConfigurationManager.SYS_CONFIG_NAME;
|
||||
import static com.netflix.config.ConfigurationManager.URL_CONFIG_NAME;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import javax.annotation.PreDestroy;
|
||||
|
||||
import org.apache.commons.configuration.ConfigurationBuilder;
|
||||
import org.apache.commons.configuration.EnvironmentConfiguration;
|
||||
import org.apache.commons.configuration.SystemConfiguration;
|
||||
@@ -20,85 +23,111 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.env.ConfigurableEnvironment;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
import com.netflix.config.ConcurrentCompositeConfiguration;
|
||||
import com.netflix.config.ConfigurationManager;
|
||||
import com.netflix.config.DynamicPropertyFactory;
|
||||
import com.netflix.config.DynamicURLConfiguration;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
@Configuration
|
||||
@ConditionalOnClass({ConcurrentCompositeConfiguration.class, ConfigurationBuilder.class})
|
||||
@ConditionalOnClass({ ConcurrentCompositeConfiguration.class, ConfigurationBuilder.class })
|
||||
public class ArchaiusAutoConfiguration {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(ArchaiusAutoConfiguration.class);
|
||||
private static final AtomicBoolean initialized = new AtomicBoolean(false);
|
||||
private static final Logger logger = LoggerFactory
|
||||
.getLogger(ArchaiusAutoConfiguration.class);
|
||||
private static final AtomicBoolean initialized = new AtomicBoolean(false);
|
||||
|
||||
@Autowired
|
||||
private ConfigurableEnvironment env;
|
||||
@Autowired
|
||||
private ConfigurableEnvironment env;
|
||||
|
||||
@Bean
|
||||
public ConfigurableEnvironmentConfiguration configurableEnvironmentConfiguration() {
|
||||
ConfigurableEnvironmentConfiguration envConfig = new ConfigurableEnvironmentConfiguration(env);
|
||||
configureArchaius(envConfig);
|
||||
return envConfig;
|
||||
}
|
||||
|
||||
@Bean
|
||||
protected ArchaiusEndpoint archaiusEndpoint() {
|
||||
return new ArchaiusEndpoint();
|
||||
}
|
||||
@PreDestroy
|
||||
public void close() {
|
||||
setStatic(ConfigurationManager.class, "instance", null);
|
||||
setStatic(ConfigurationManager.class, "customConfigurationInstalled", false);
|
||||
setStatic(DynamicPropertyFactory.class, "config", null);
|
||||
setStatic(DynamicPropertyFactory.class, "initializedWithDefaultConfig", false);
|
||||
initialized.compareAndSet(true, false);
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
@Bean
|
||||
public ConfigurableEnvironmentConfiguration configurableEnvironmentConfiguration() {
|
||||
ConfigurableEnvironmentConfiguration envConfig = new ConfigurableEnvironmentConfiguration(
|
||||
env);
|
||||
configureArchaius(envConfig);
|
||||
return envConfig;
|
||||
}
|
||||
|
||||
@Bean
|
||||
protected ArchaiusEndpoint archaiusEndpoint() {
|
||||
return new ArchaiusEndpoint();
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
protected void configureArchaius(ConfigurableEnvironmentConfiguration envConfig) {
|
||||
if (initialized.compareAndSet(false, true)) {
|
||||
String appName = env.getProperty("spring.application.name");
|
||||
if (appName == null) {
|
||||
appName = "application";
|
||||
logger.warn("No spring.application.name found, defaulting to 'application'");
|
||||
}
|
||||
//this is deprecated, but currently it seams the only way to set it initially
|
||||
System.setProperty(DEPLOYMENT_APPLICATION_ID_PROPERTY, appName);
|
||||
if (initialized.compareAndSet(false, true)) {
|
||||
String appName = env.getProperty("spring.application.name");
|
||||
if (appName == null) {
|
||||
appName = "application";
|
||||
logger.warn("No spring.application.name found, defaulting to 'application'");
|
||||
}
|
||||
// this is deprecated, but currently it seams the only way to set it initially
|
||||
System.setProperty(DEPLOYMENT_APPLICATION_ID_PROPERTY, appName);
|
||||
|
||||
//TODO: support for other DeploymentContexts
|
||||
// TODO: support for other DeploymentContexts
|
||||
|
||||
ConcurrentCompositeConfiguration config = new ConcurrentCompositeConfiguration();
|
||||
ConcurrentCompositeConfiguration config = new ConcurrentCompositeConfiguration();
|
||||
|
||||
//support to add other Configurations (Jdbc, DynamoDb, Zookeeper, jclouds, etc...)
|
||||
/*if (factories != null && !factories.isEmpty()) {
|
||||
for (PropertiesSourceFactory factory: factories) {
|
||||
config.addConfiguration(factory.getConfiguration(), factory.getName());
|
||||
}
|
||||
}*/
|
||||
config.addConfiguration(envConfig, ConfigurableEnvironmentConfiguration.class.getSimpleName());
|
||||
// support to add other Configurations (Jdbc, DynamoDb, Zookeeper, jclouds,
|
||||
// etc...)
|
||||
/*
|
||||
* if (factories != null && !factories.isEmpty()) { for
|
||||
* (PropertiesSourceFactory factory: factories) {
|
||||
* config.addConfiguration(factory.getConfiguration(), factory.getName()); } }
|
||||
*/
|
||||
config.addConfiguration(envConfig,
|
||||
ConfigurableEnvironmentConfiguration.class.getSimpleName());
|
||||
|
||||
//below come from ConfigurationManager.createDefaultConfigInstance()
|
||||
DynamicURLConfiguration defaultURLConfig = new DynamicURLConfiguration();
|
||||
try {
|
||||
config.addConfiguration(defaultURLConfig, URL_CONFIG_NAME);
|
||||
} catch (Throwable e) {
|
||||
logger.error("Cannot create config from " + defaultURLConfig, e);
|
||||
}
|
||||
// below come from ConfigurationManager.createDefaultConfigInstance()
|
||||
DynamicURLConfiguration defaultURLConfig = new DynamicURLConfiguration();
|
||||
try {
|
||||
config.addConfiguration(defaultURLConfig, URL_CONFIG_NAME);
|
||||
}
|
||||
catch (Throwable e) {
|
||||
logger.error("Cannot create config from " + defaultURLConfig, e);
|
||||
}
|
||||
|
||||
//TODO: sys/env above urls?
|
||||
if (!Boolean.getBoolean(DISABLE_DEFAULT_SYS_CONFIG)) {
|
||||
SystemConfiguration sysConfig = new SystemConfiguration();
|
||||
config.addConfiguration(sysConfig, SYS_CONFIG_NAME);
|
||||
}
|
||||
if (!Boolean.getBoolean(DISABLE_DEFAULT_ENV_CONFIG)) {
|
||||
EnvironmentConfiguration environmentConfiguration = new EnvironmentConfiguration();
|
||||
config.addConfiguration(environmentConfiguration, ENV_CONFIG_NAME);
|
||||
}
|
||||
// TODO: sys/env above urls?
|
||||
if (!Boolean.getBoolean(DISABLE_DEFAULT_SYS_CONFIG)) {
|
||||
SystemConfiguration sysConfig = new SystemConfiguration();
|
||||
config.addConfiguration(sysConfig, SYS_CONFIG_NAME);
|
||||
}
|
||||
if (!Boolean.getBoolean(DISABLE_DEFAULT_ENV_CONFIG)) {
|
||||
EnvironmentConfiguration environmentConfiguration = new EnvironmentConfiguration();
|
||||
config.addConfiguration(environmentConfiguration, ENV_CONFIG_NAME);
|
||||
}
|
||||
|
||||
ConcurrentCompositeConfiguration appOverrideConfig = new ConcurrentCompositeConfiguration();
|
||||
config.addConfiguration(appOverrideConfig, APPLICATION_PROPERTIES);
|
||||
config.setContainerConfigurationIndex(config.getIndexOfConfiguration(appOverrideConfig));
|
||||
ConcurrentCompositeConfiguration appOverrideConfig = new ConcurrentCompositeConfiguration();
|
||||
config.addConfiguration(appOverrideConfig, APPLICATION_PROPERTIES);
|
||||
config.setContainerConfigurationIndex(config
|
||||
.getIndexOfConfiguration(appOverrideConfig));
|
||||
|
||||
ConfigurationManager.install(config);
|
||||
}
|
||||
else {
|
||||
// TODO: reinstall ConfigurationManager
|
||||
logger.warn("Netflix ConfigurationManager has already been installed, unable to re-install");
|
||||
}
|
||||
}
|
||||
|
||||
private static void setStatic(Class<?> type, String name, Object value) {
|
||||
// Hack a private static field
|
||||
Field field = ReflectionUtils.findField(type, name);
|
||||
ReflectionUtils.makeAccessible(field);
|
||||
ReflectionUtils.setField(field, null, value);
|
||||
}
|
||||
|
||||
ConfigurationManager.install(config);
|
||||
} else {
|
||||
//TODO: reinstall ConfigurationManager
|
||||
logger.warn("Netflix ConfigurationManager has already been installed, unable to re-install");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright 2013-2014 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.netflix.archaius;
|
||||
|
||||
import org.apache.commons.configuration.AbstractConfiguration;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
|
||||
import com.netflix.config.ConfigurationManager;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
public class ArchaiusDelegatingProxyUtils {
|
||||
|
||||
public static String APPLICATION_CONTEXT = ApplicationContext.class.getName();
|
||||
|
||||
public static <T> T getNamedInstance(Class<T> type, String name) {
|
||||
ApplicationContext context = (ApplicationContext) ConfigurationManager.getConfigInstance().getProperty(APPLICATION_CONTEXT);
|
||||
return context!=null && context.containsBean(name) ? context.getBean(name, type) : null;
|
||||
}
|
||||
|
||||
public static <T> T getInstanceWithPrefix(Class<T> type, String prefix) {
|
||||
String name = prefix + type.getSimpleName();
|
||||
return getNamedInstance(type, name);
|
||||
}
|
||||
|
||||
public static void addApplicationContext(ConfigurableApplicationContext context) {
|
||||
AbstractConfiguration config = ConfigurationManager.getConfigInstance();
|
||||
config .clearProperty(APPLICATION_CONTEXT);
|
||||
config.setProperty(APPLICATION_CONTEXT, context);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -54,6 +54,7 @@ public class FeignConfiguration {
|
||||
|
||||
protected <T> T loadBalance(Feign.Builder builder, Class<T> type, String schemeName) {
|
||||
String name = URI.create(schemeName).getHost();
|
||||
// TODO: This should be transparent
|
||||
ribbonClientPreprocessor.preprocess(name);
|
||||
|
||||
if(ribbonClient != null) {
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright 2013-2014 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.netflix.ribbon;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
@Configuration
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ ElementType.TYPE })
|
||||
@Documented
|
||||
@Import(RibbonClientConfigurationRegistrar.class)
|
||||
public @interface EnableRibbonClient {
|
||||
|
||||
RibbonClient[] value() default {};
|
||||
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
package org.springframework.cloud.netflix.ribbon;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
@@ -16,7 +15,6 @@ import org.springframework.http.client.ClientHttpRequestInterceptor;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
import com.netflix.client.IClient;
|
||||
import com.netflix.loadbalancer.BaseLoadBalancer;
|
||||
|
||||
/**
|
||||
* Auto configuration for Ribbon (client side load balancing)
|
||||
@@ -25,15 +23,18 @@ import com.netflix.loadbalancer.BaseLoadBalancer;
|
||||
*/
|
||||
@Configuration
|
||||
@ConditionalOnClass(IClient.class)
|
||||
@EnableRibbonClient
|
||||
@AutoConfigureAfter(EurekaClientAutoConfiguration.class)
|
||||
public class RibbonAutoConfiguration {
|
||||
|
||||
@Autowired(required = false)
|
||||
private List<BaseLoadBalancer> balancers = Collections.emptyList();
|
||||
@Autowired(required=false)
|
||||
private List<RibbonClientSpecification> configurations = new ArrayList<>();
|
||||
|
||||
@Bean
|
||||
@Bean
|
||||
public SpringClientFactory springClientFactory() {
|
||||
return new SpringClientFactory();
|
||||
SpringClientFactory factory = new SpringClientFactory();
|
||||
factory.setConfigurations(configurations);
|
||||
return factory;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@@ -48,8 +49,8 @@ public class RibbonAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(LoadBalancerClient.class)
|
||||
public LoadBalancerClient loadBalancerClient(RibbonClientPreprocessor clientPreprocessor) {
|
||||
return new RibbonLoadBalancerClient(clientPreprocessor, springClientFactory(), balancers);
|
||||
public LoadBalancerClient loadBalancerClient() {
|
||||
return new RibbonLoadBalancerClient(springClientFactory());
|
||||
}
|
||||
|
||||
@Bean
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright 2013-2014 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.netflix.ribbon;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
@Target(ElementType.TYPE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
public @interface RibbonClient {
|
||||
String name();
|
||||
Class<?>[] configuration() default {};
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
/*
|
||||
* Copyright 2013-2014 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.netflix.ribbon;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
|
||||
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.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.cloud.netflix.ribbon.eureka.DomainExtractingServerList;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import com.netflix.client.config.DefaultClientConfigImpl;
|
||||
import com.netflix.client.config.IClientConfig;
|
||||
import com.netflix.discovery.EurekaClientConfig;
|
||||
import com.netflix.loadbalancer.DynamicServerListLoadBalancer;
|
||||
import com.netflix.loadbalancer.ILoadBalancer;
|
||||
import com.netflix.loadbalancer.Server;
|
||||
import com.netflix.loadbalancer.ServerList;
|
||||
import com.netflix.loadbalancer.ServerListFilter;
|
||||
import com.netflix.loadbalancer.ZoneAffinityServerListFilter;
|
||||
import com.netflix.loadbalancer.ZoneAwareLoadBalancer;
|
||||
import com.netflix.niws.client.http.RestClient;
|
||||
import com.netflix.servo.monitor.Monitors;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
@Configuration
|
||||
@EnableConfigurationProperties
|
||||
public class RibbonClientConfiguration {
|
||||
|
||||
@Value("${ribbon.client.name}")
|
||||
private String name = "client";
|
||||
|
||||
@Autowired(required = false)
|
||||
private EurekaClientConfig eurekaClientConfig;
|
||||
|
||||
@Autowired(required = false)
|
||||
private RibbonClientPreprocessor preprocessor;
|
||||
|
||||
// TODO: maybe re-instate autowired load balancers: identified by name they could be
|
||||
// associated with ribbon clients
|
||||
|
||||
@PostConstruct
|
||||
public void init() {
|
||||
if (preprocessor!=null) {
|
||||
preprocessor.preprocess(name);
|
||||
}
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public IClientConfig ribbonClientConfig() {
|
||||
DefaultClientConfigImpl config = new DefaultClientConfigImpl();
|
||||
config.loadProperties(name);
|
||||
return config;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public RestClient ribbonRestClient(IClientConfig config, ILoadBalancer loadBalancer) {
|
||||
RestClient client = new RestClient(config);
|
||||
client.setLoadBalancer(loadBalancer);
|
||||
Monitors.registerObject("Client_" + name, client);
|
||||
return client;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public ILoadBalancer ribbonLoadBalancer(IClientConfig config, ServerListFilter<Server> filter) {
|
||||
ZoneAwareLoadBalancer<Server> balancer = new ZoneAwareLoadBalancer<Server>(config);
|
||||
wrapServerList(balancer);
|
||||
balancer.setFilter(filter);
|
||||
return balancer;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public ServerListFilter<Server> ribbonServerListFilter(IClientConfig config) {
|
||||
return new ZoneAffinityServerListFilter<Server>(config);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public RibbonLoadBalancerContext ribbonLoadBalancerContext(ILoadBalancer loadBalancer, IClientConfig config) {
|
||||
return new RibbonLoadBalancerContext(loadBalancer, config);
|
||||
}
|
||||
|
||||
private void wrapServerList(ILoadBalancer balancer) {
|
||||
if (balancer instanceof DynamicServerListLoadBalancer) {
|
||||
@SuppressWarnings("unchecked")
|
||||
DynamicServerListLoadBalancer<Server> dynamic = (DynamicServerListLoadBalancer<Server>) balancer;
|
||||
ServerList<Server> list = dynamic.getServerListImpl();
|
||||
if (!(list instanceof DomainExtractingServerList)) {
|
||||
// This is optional: you can use the native Eureka AWS features as long as
|
||||
// the server zone is populated. TODO: verify that we back off if AWS
|
||||
// metadata *is* available.
|
||||
// @see com.netflix.appinfo.AmazonInfo.Builder
|
||||
dynamic.setServerListImpl(new DomainExtractingServerList(list, dynamic
|
||||
.getClientConfig()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright 2013-2014 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.netflix.ribbon;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
||||
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
|
||||
import org.springframework.core.annotation.AnnotationAttributes;
|
||||
import org.springframework.core.type.AnnotationMetadata;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
public class RibbonClientConfigurationRegistrar implements ImportBeanDefinitionRegistrar {
|
||||
|
||||
@Override
|
||||
public void registerBeanDefinitions(AnnotationMetadata metadata,
|
||||
BeanDefinitionRegistry registry) {
|
||||
Map<String, Object> attrs = metadata.getAnnotationAttributes(
|
||||
EnableRibbonClient.class.getName(), true);
|
||||
if (attrs.containsKey("value")) {
|
||||
AnnotationAttributes[] clients = (AnnotationAttributes[]) attrs.get("value");
|
||||
for (AnnotationAttributes attr : clients) {
|
||||
BeanDefinitionBuilder builder = BeanDefinitionBuilder
|
||||
.genericBeanDefinition(RibbonClientSpecification.class);
|
||||
builder.addConstructorArgValue(attr.get("name"));
|
||||
builder.addConstructorArgValue(attr.get("configuration"));
|
||||
registry.registerBeanDefinition(attr.get("name")
|
||||
+ "RibbonClientSpecification", builder.getBeanDefinition());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright 2013-2014 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.netflix.ribbon;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
public class RibbonClientSpecification {
|
||||
|
||||
private String name;
|
||||
private Class<?>[] configuration;
|
||||
|
||||
}
|
||||
@@ -1,20 +1,17 @@
|
||||
package org.springframework.cloud.netflix.ribbon;
|
||||
|
||||
import com.google.common.base.Throwables;
|
||||
import com.netflix.loadbalancer.BaseLoadBalancer;
|
||||
import com.netflix.loadbalancer.ILoadBalancer;
|
||||
import com.netflix.loadbalancer.Server;
|
||||
import com.netflix.loadbalancer.ServerStats;
|
||||
import com.netflix.servo.monitor.Stopwatch;
|
||||
import java.net.URI;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.springframework.cloud.client.ServiceInstance;
|
||||
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
|
||||
import org.springframework.cloud.client.loadbalancer.LoadBalancerRequest;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import com.google.common.base.Throwables;
|
||||
import com.netflix.loadbalancer.ILoadBalancer;
|
||||
import com.netflix.loadbalancer.Server;
|
||||
import com.netflix.loadbalancer.ServerStats;
|
||||
import com.netflix.servo.monitor.Stopwatch;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
@@ -22,25 +19,16 @@ import java.util.concurrent.TimeUnit;
|
||||
*/
|
||||
public class RibbonLoadBalancerClient implements LoadBalancerClient {
|
||||
|
||||
private RibbonClientPreprocessor ribbonClientPreprocessor;
|
||||
|
||||
private SpringClientFactory clientFactory;
|
||||
|
||||
private Map<String, ILoadBalancer> balancers = new ConcurrentHashMap<>();
|
||||
private Map<String, RibbonLoadBalancerContext> contexts = new ConcurrentHashMap<>();
|
||||
|
||||
public RibbonLoadBalancerClient(RibbonClientPreprocessor ribbonClientPreprocessor, SpringClientFactory clientFactory, List<BaseLoadBalancer> balancers) {
|
||||
this.ribbonClientPreprocessor = ribbonClientPreprocessor;
|
||||
public RibbonLoadBalancerClient(SpringClientFactory clientFactory) {
|
||||
this.clientFactory = clientFactory;
|
||||
for (BaseLoadBalancer balancer : balancers) {
|
||||
this.balancers.put(balancer.getName(), balancer);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public URI reconstructURI(ServiceInstance instance, URI original) {
|
||||
String serviceId = instance.getServiceId();
|
||||
RibbonLoadBalancerContext context = getOrCreateLoadBalancerContext(serviceId, getLoadBalancer(serviceId));
|
||||
RibbonLoadBalancerContext context = clientFactory.getLoadBalancerContext(serviceId);
|
||||
Server server = new Server(instance.getHost(), instance.getPort());
|
||||
return context.reconstructURIWithServer(server, original);
|
||||
}
|
||||
@@ -53,7 +41,7 @@ public class RibbonLoadBalancerClient implements LoadBalancerClient {
|
||||
@Override
|
||||
public <T> T execute(String serviceId, LoadBalancerRequest<T> request) {
|
||||
ILoadBalancer loadBalancer = getLoadBalancer(serviceId);
|
||||
RibbonLoadBalancerContext context = getOrCreateLoadBalancerContext(serviceId, loadBalancer);
|
||||
RibbonLoadBalancerContext context = clientFactory.getLoadBalancerContext(serviceId);
|
||||
Server server = getServer(serviceId, loadBalancer);
|
||||
RibbonServer ribbonServer = new RibbonServer(serviceId, server);
|
||||
|
||||
@@ -79,18 +67,10 @@ public class RibbonLoadBalancerClient implements LoadBalancerClient {
|
||||
context.noteRequestCompletion(serverStats, entity, exception, duration, null/*errorHandler*/);
|
||||
}
|
||||
|
||||
protected RibbonLoadBalancerContext getOrCreateLoadBalancerContext(String serviceId, ILoadBalancer loadBalancer) {
|
||||
RibbonLoadBalancerContext context = contexts.get(serviceId);
|
||||
if (context == null) {
|
||||
context = new RibbonLoadBalancerContext(loadBalancer);
|
||||
contexts.put(serviceId, context);
|
||||
}
|
||||
return context;
|
||||
}
|
||||
|
||||
protected Server getServer(String serviceId) {
|
||||
return getServer(serviceId, getLoadBalancer(serviceId));
|
||||
}
|
||||
|
||||
protected Server getServer(String serviceId, ILoadBalancer loadBalancer) {
|
||||
Server server = loadBalancer.chooseServer("default");
|
||||
if (server == null) {
|
||||
@@ -101,12 +81,7 @@ public class RibbonLoadBalancerClient implements LoadBalancerClient {
|
||||
}
|
||||
|
||||
protected ILoadBalancer getLoadBalancer(String serviceId) {
|
||||
ribbonClientPreprocessor.preprocess(serviceId);
|
||||
ILoadBalancer loadBalancer = this.balancers.get(serviceId);
|
||||
if (loadBalancer == null) {
|
||||
loadBalancer = clientFactory.getNamedLoadBalancer(serviceId);
|
||||
}
|
||||
return loadBalancer;
|
||||
return clientFactory.getLoadBalancer(serviceId);
|
||||
}
|
||||
|
||||
protected static class RibbonServer implements ServiceInstance {
|
||||
|
||||
@@ -1,238 +1,153 @@
|
||||
package org.springframework.cloud.netflix.ribbon;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.BeanFactoryUtils;
|
||||
import org.springframework.beans.factory.DisposableBean;
|
||||
import org.springframework.boot.autoconfigure.PropertyPlaceholderAutoConfiguration;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
import org.springframework.core.env.MapPropertySource;
|
||||
|
||||
import com.netflix.client.AbstractLoadBalancerAwareClient;
|
||||
import com.netflix.client.ClientException;
|
||||
import com.netflix.client.IClient;
|
||||
import com.netflix.client.IClientConfigAware;
|
||||
import com.netflix.client.config.CommonClientConfigKey;
|
||||
import com.netflix.client.config.DefaultClientConfigImpl;
|
||||
import com.netflix.client.config.IClientConfig;
|
||||
import com.netflix.loadbalancer.ILoadBalancer;
|
||||
import com.netflix.servo.monitor.Monitors;
|
||||
|
||||
/**
|
||||
* A factory that creates client, load balancer and client configuration instances from properties. It also keeps mappings of client names to
|
||||
* the created instances.
|
||||
* A factory that creates client, load balancer and client configuration instances. It
|
||||
* creates a Spring ApplicationContext per client name, and extracts the beans that it
|
||||
* needs from there.
|
||||
*
|
||||
*/
|
||||
@Slf4j
|
||||
public class SpringClientFactory {
|
||||
public class SpringClientFactory implements DisposableBean, ApplicationContextAware {
|
||||
|
||||
private Map<String, IClient<?,?>> simpleClientMap = new ConcurrentHashMap<>();
|
||||
private Map<String, ILoadBalancer> namedLBMap = new ConcurrentHashMap<>();
|
||||
private ConcurrentHashMap<String, IClientConfig> namedConfig = new ConcurrentHashMap<>();
|
||||
private Map<String, AnnotationConfigApplicationContext> contexts = new ConcurrentHashMap<>();
|
||||
private Map<String, RibbonClientSpecification> configurations = new ConcurrentHashMap<>();
|
||||
private ApplicationContext parent;
|
||||
|
||||
/**
|
||||
* Utility method to create client and load balancer (if enabled in client config) given the name and client config.
|
||||
* Instances are created using reflection (see {@link #instantiateInstanceWithClientConfig(String, IClientConfig)}
|
||||
*
|
||||
* @param restClientName
|
||||
* @param clientConfig
|
||||
* @throws ClientException if any errors occurs in the process, or if the client with the same name already exists
|
||||
*/
|
||||
public synchronized IClient<?, ?> registerClientFromProperties(String restClientName, IClientConfig clientConfig) throws ClientException {
|
||||
IClient<?, ?> client;
|
||||
ILoadBalancer loadBalancer = null;
|
||||
if (simpleClientMap.get(restClientName) != null) {
|
||||
throw new ClientException(
|
||||
ClientException.ErrorType.GENERAL,
|
||||
"A Rest Client with this name is already registered. Please use a different name");
|
||||
}
|
||||
try {
|
||||
String clientClassName = (String) clientConfig.get(CommonClientConfigKey.ClientClassName);
|
||||
client = (IClient<?, ?>) instantiateInstanceWithClientConfig(clientClassName, clientConfig);
|
||||
boolean initializeNFLoadBalancer = Boolean.parseBoolean(clientConfig.get(
|
||||
CommonClientConfigKey.InitializeNFLoadBalancer, DefaultClientConfigImpl.DEFAULT_ENABLE_LOADBALANCER).toString());
|
||||
if (initializeNFLoadBalancer) {
|
||||
loadBalancer = getNamedLoadBalancer(restClientName, clientConfig.getClass());
|
||||
}
|
||||
if (client instanceof AbstractLoadBalancerAwareClient) {
|
||||
((AbstractLoadBalancerAwareClient<?,?>) client).setLoadBalancer(loadBalancer);
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
String message = "Unable to InitializeAndAssociateNFLoadBalancer set for RestClient:"
|
||||
+ restClientName;
|
||||
log.warn(message);
|
||||
throw new ClientException(ClientException.ErrorType.CONFIGURATION,
|
||||
message, e);
|
||||
}
|
||||
simpleClientMap.put(restClientName, client);
|
||||
@Override
|
||||
public void setApplicationContext(ApplicationContext parent) throws BeansException {
|
||||
this.parent = parent;
|
||||
}
|
||||
|
||||
public void setConfigurations(List<RibbonClientSpecification> configurations) {
|
||||
for (RibbonClientSpecification client : configurations) {
|
||||
this.configurations.put(client.getName(), client);
|
||||
}
|
||||
}
|
||||
|
||||
Monitors.registerObject("Client_" + restClientName, client);
|
||||
@Override
|
||||
public void destroy() {
|
||||
Collection<AnnotationConfigApplicationContext> values = contexts.values();
|
||||
contexts.clear();
|
||||
for (AnnotationConfigApplicationContext context : values) {
|
||||
context.close();
|
||||
}
|
||||
}
|
||||
|
||||
log.info("Client Registered:" + client.toString());
|
||||
return client;
|
||||
}
|
||||
/**
|
||||
* Get the rest client associated with the name.
|
||||
*
|
||||
* @throws RuntimeException if any error occurs
|
||||
*/
|
||||
public <C extends IClient<?, ?>> C getClient(String name, Class<C> clientClass) {
|
||||
return getInstance(name, clientClass);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the named client from map if already created. Otherwise creates the client using the configuration returned by {@link #getNamedConfig(String)}.
|
||||
*
|
||||
* @throws RuntimeException if an error occurs in creating the client.
|
||||
*/
|
||||
public synchronized IClient<?,?> getNamedClient(String name) {
|
||||
return getNamedClient(name, DefaultClientConfigImpl.class);
|
||||
}
|
||||
/**
|
||||
* Get the load balancer associated with the name.
|
||||
*
|
||||
* @throws RuntimeException if any error occurs
|
||||
*/
|
||||
public ILoadBalancer getLoadBalancer(String name) {
|
||||
return getInstance(name, ILoadBalancer.class);
|
||||
}
|
||||
|
||||
public synchronized <C extends IClient<?,?>> C namedClient(String name, Class<C> clientClass) {
|
||||
return clientClass.cast(getNamedClient(name, DefaultClientConfigImpl.class));
|
||||
}
|
||||
/**
|
||||
* Get the load balancer context associated with the name.
|
||||
*
|
||||
* @throws RuntimeException if any error occurs
|
||||
*/
|
||||
public RibbonLoadBalancerContext getLoadBalancerContext(String serviceId) {
|
||||
return getInstance(serviceId, RibbonLoadBalancerContext.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the named client from map if already created. Otherwise creates the client using the configuration returned by {@link #createNamedClient(String, Class)}.
|
||||
*
|
||||
* @throws RuntimeException if an error occurs in creating the client.
|
||||
*/
|
||||
public synchronized IClient<?,?> getNamedClient(String name, Class<? extends IClientConfig> configClass) {
|
||||
if (simpleClientMap.get(name) != null) {
|
||||
return simpleClientMap.get(name);
|
||||
}
|
||||
try {
|
||||
return createNamedClient(name, configClass);
|
||||
} catch (ClientException e) {
|
||||
throw new RuntimeException("Unable to create client", e);
|
||||
}
|
||||
}
|
||||
private AnnotationConfigApplicationContext getContext(String name) {
|
||||
if (!contexts.containsKey(name)) {
|
||||
synchronized (contexts) {
|
||||
if (!contexts.containsKey(name)) {
|
||||
contexts.put(name, createContext(name));
|
||||
}
|
||||
}
|
||||
}
|
||||
return contexts.get(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a named client using a IClientConfig instance created off the configClass class object passed in as the parameter.
|
||||
*
|
||||
* @throws ClientException if any error occurs, or if the client with the same name already exists
|
||||
*/
|
||||
public synchronized IClient<?,?> createNamedClient(String name, Class<? extends IClientConfig> configClass) throws ClientException {
|
||||
IClientConfig config = getNamedConfig(name, configClass);
|
||||
return registerClientFromProperties(name, config);
|
||||
}
|
||||
private AnnotationConfigApplicationContext createContext(String name) {
|
||||
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
|
||||
if (configurations.containsKey(name)) {
|
||||
for (Class<?> configuration : configurations.get(name).getConfiguration()) {
|
||||
context.register(configuration);
|
||||
}
|
||||
}
|
||||
context.register(PropertyPlaceholderAutoConfiguration.class,
|
||||
RibbonClientConfiguration.class);
|
||||
context.getEnvironment()
|
||||
.getPropertySources()
|
||||
.addFirst(
|
||||
new MapPropertySource("ribbon",
|
||||
Collections.<String, Object> singletonMap(
|
||||
"ribbon.client.name", name)));
|
||||
if (parent != null) {
|
||||
// Uses Environment from parent as well as beans
|
||||
context.setParent(parent);
|
||||
}
|
||||
context.refresh();
|
||||
return context;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the load balancer associated with the name, or create one with an instance {@link DefaultClientConfigImpl} if does not exist
|
||||
*
|
||||
* @throws RuntimeException if any error occurs
|
||||
*/
|
||||
public synchronized ILoadBalancer getNamedLoadBalancer(String name) {
|
||||
return getNamedLoadBalancer(name, DefaultClientConfigImpl.class);
|
||||
}
|
||||
private <C> C instantiateWithConfig(AnnotationConfigApplicationContext context,
|
||||
Class<C> clazz, IClientConfig config) {
|
||||
C result = null;
|
||||
if (IClientConfigAware.class.isAssignableFrom(clazz)) {
|
||||
IClientConfigAware obj = (IClientConfigAware) BeanUtils.instantiate(clazz);
|
||||
obj.initWithNiwsConfig(config);
|
||||
@SuppressWarnings("unchecked")
|
||||
C value = (C) obj;
|
||||
result = value;
|
||||
}
|
||||
else {
|
||||
try {
|
||||
if (clazz.getConstructor(IClientConfig.class) != null) {
|
||||
result = clazz.getConstructor(IClientConfig.class)
|
||||
.newInstance(config);
|
||||
}
|
||||
else {
|
||||
result = BeanUtils.instantiate(clazz);
|
||||
}
|
||||
}
|
||||
catch (Throwable e) { // NOPMD
|
||||
}
|
||||
}
|
||||
context.getAutowireCapableBeanFactory().autowireBean(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the load balancer associated with the name, or create one with an instance of configClass if does not exist
|
||||
*
|
||||
* @throws RuntimeException if any error occurs
|
||||
* @see #registerNamedLoadBalancerFromProperties(String, Class)
|
||||
*/
|
||||
public synchronized ILoadBalancer getNamedLoadBalancer(String name, Class<? extends IClientConfig> configClass) {
|
||||
ILoadBalancer lb = namedLBMap.get(name);
|
||||
if (lb != null) {
|
||||
return lb;
|
||||
} else {
|
||||
try {
|
||||
lb = registerNamedLoadBalancerFromProperties(name, configClass);
|
||||
} catch (ClientException e) {
|
||||
throw new RuntimeException("Unable to create load balancer", e);
|
||||
}
|
||||
return lb;
|
||||
}
|
||||
}
|
||||
private <C> C getInstance(String name, Class<C> type) {
|
||||
AnnotationConfigApplicationContext context = getContext(name);
|
||||
if (BeanFactoryUtils.beanNamesForTypeIncludingAncestors(context, type).length > 0) {
|
||||
return context.getBean(type);
|
||||
}
|
||||
IClientConfig config = getInstance(name, IClientConfig.class);
|
||||
return instantiateWithConfig(context, type, config);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create and register a load balancer with the name and given the class of configClass.
|
||||
*
|
||||
* @throws ClientException if load balancer with the same name already exists or any error occurs
|
||||
* @see #instantiateInstanceWithClientConfig(String, IClientConfig)
|
||||
*/
|
||||
public ILoadBalancer registerNamedLoadBalancerFromclientConfig(String name, IClientConfig clientConfig) throws ClientException {
|
||||
if (namedLBMap.get(name) != null) {
|
||||
throw new ClientException("LoadBalancer for name " + name + " already exists");
|
||||
}
|
||||
ILoadBalancer lb = null;
|
||||
try {
|
||||
String loadBalancerClassName = (String) clientConfig.get(CommonClientConfigKey.NFLoadBalancerClassName);
|
||||
lb = (ILoadBalancer) instantiateInstanceWithClientConfig(loadBalancerClassName, clientConfig);
|
||||
namedLBMap.put(name, lb);
|
||||
log.info("Client:" + name
|
||||
+ " instantiated a LoadBalancer:" + lb.toString());
|
||||
return lb;
|
||||
} catch (Exception e) {
|
||||
throw new ClientException("Unable to instantiate/associate LoadBalancer with Client:" + name, e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create and register a load balancer with the name and given the class of configClass.
|
||||
*
|
||||
* @throws ClientException if load balancer with the same name already exists or any error occurs
|
||||
* @see #instantiateInstanceWithClientConfig(String, IClientConfig)
|
||||
*/
|
||||
public synchronized ILoadBalancer registerNamedLoadBalancerFromProperties(String name, Class<? extends IClientConfig> configClass) throws ClientException {
|
||||
if (namedLBMap.get(name) != null) {
|
||||
throw new ClientException("LoadBalancer for name " + name + " already exists");
|
||||
}
|
||||
IClientConfig clientConfig = getNamedConfig(name, configClass);
|
||||
return registerNamedLoadBalancerFromclientConfig(name, clientConfig);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates instance related to client framework using reflection. It first checks if the object is an instance of
|
||||
* {@link IClientConfigAware} and if so invoke {@link IClientConfigAware#initWithNiwsConfig(IClientConfig)}. If that does not
|
||||
* apply, it tries to find if there is a constructor with {@link IClientConfig} as a parameter and if so invoke that constructor. If neither applies,
|
||||
* it simply invokes the no-arg constructor and ignores the clientConfig parameter.
|
||||
*
|
||||
* @param className Class name of the object
|
||||
* @param clientConfig IClientConfig object used for initialization.
|
||||
*/
|
||||
public Object instantiateInstanceWithClientConfig(String className, IClientConfig clientConfig)
|
||||
throws InstantiationException, IllegalAccessException, ClassNotFoundException {
|
||||
Class<?> clazz = Class.forName(className);
|
||||
if (IClientConfigAware.class.isAssignableFrom(clazz)) {
|
||||
IClientConfigAware obj = (IClientConfigAware) clazz.newInstance();
|
||||
obj.initWithNiwsConfig(clientConfig);
|
||||
return obj;
|
||||
} else {
|
||||
try {
|
||||
if (clazz.getConstructor(IClientConfig.class) != null) {
|
||||
return clazz.getConstructor(IClientConfig.class).newInstance(clientConfig);
|
||||
}
|
||||
} catch (Throwable e) { // NOPMD
|
||||
}
|
||||
}
|
||||
log.warn("Class " + className + " neither implements IClientConfigAware nor provides a constructor with IClientConfig as the parameter. Only default constructor will be used.");
|
||||
return clazz.newInstance();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the client configuration given the name or create one with {@link DefaultClientConfigImpl} if it does not exist.
|
||||
*
|
||||
* @see #getNamedConfig(String, Class)
|
||||
*/
|
||||
public IClientConfig getNamedConfig(String name) {
|
||||
return getNamedConfig(name, DefaultClientConfigImpl.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the client configuration given the name or create one with clientConfigClass if it does not exist. An instance of IClientConfig
|
||||
* is created and {@link IClientConfig#loadProperties(String)} will be called.
|
||||
*/
|
||||
public IClientConfig getNamedConfig(String name, Class<? extends IClientConfig> clientConfigClass) {
|
||||
IClientConfig config = namedConfig.get(name);
|
||||
if (config != null) {
|
||||
return config;
|
||||
} else {
|
||||
try {
|
||||
config = clientConfigClass.newInstance();
|
||||
config.loadProperties(name);
|
||||
} catch (Throwable e) {
|
||||
log.error("Unable to create client config instance", e);
|
||||
return null;
|
||||
}
|
||||
config.loadProperties(name);
|
||||
IClientConfig old = namedConfig.putIfAbsent(name, config);
|
||||
if (old != null) {
|
||||
config = old;
|
||||
}
|
||||
return config;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,41 +6,34 @@ import static com.netflix.client.config.CommonClientConfigKey.NFLoadBalancerRule
|
||||
import static com.netflix.client.config.CommonClientConfigKey.NIWSServerListClassName;
|
||||
import static com.netflix.client.config.CommonClientConfigKey.NIWSServerListFilterClassName;
|
||||
|
||||
import com.netflix.config.DynamicPropertyFactory;
|
||||
import com.netflix.config.DynamicStringProperty;
|
||||
import org.springframework.cloud.netflix.ribbon.RibbonClientPreprocessor;
|
||||
import org.springframework.cloud.netflix.ribbon.SpringClientFactory;
|
||||
|
||||
import com.netflix.config.ConfigurationManager;
|
||||
import com.netflix.config.DeploymentContext.ContextKey;
|
||||
import com.netflix.config.DynamicPropertyFactory;
|
||||
import com.netflix.config.DynamicStringProperty;
|
||||
import com.netflix.discovery.EurekaClientConfig;
|
||||
import com.netflix.loadbalancer.DynamicServerListLoadBalancer;
|
||||
import com.netflix.loadbalancer.ILoadBalancer;
|
||||
import com.netflix.loadbalancer.Server;
|
||||
import com.netflix.loadbalancer.ServerList;
|
||||
import com.netflix.loadbalancer.ZoneAvoidanceRule;
|
||||
import com.netflix.niws.loadbalancer.DiscoveryEnabledNIWSServerList;
|
||||
|
||||
/**
|
||||
* Preprocessor that configures defaults for eureka-discovered ribbon clients.
|
||||
* Such as: <code>@zone</code>, NIWSServerListClassName, DeploymentContextBasedVipAddresses,
|
||||
* NFLoadBalancerRuleClassName, NIWSServerListFilterClassName and more
|
||||
* Preprocessor that configures defaults for eureka-discovered ribbon clients. Such as:
|
||||
* <code>@zone</code>, NIWSServerListClassName, DeploymentContextBasedVipAddresses,
|
||||
* NFLoadBalancerRuleClassName, NIWSServerListFilterClassName and more
|
||||
*
|
||||
* @author Spencer Gibb
|
||||
* @author Dave Syer
|
||||
*/
|
||||
public class EurekaRibbonClientPreprocessor implements RibbonClientPreprocessor {
|
||||
|
||||
protected static final String VALUE_NOT_SET = "__not__set__";
|
||||
protected static final String DEFAULT_NAMESPACE = "ribbon";
|
||||
protected static final String VALUE_NOT_SET = "__not__set__";
|
||||
protected static final String DEFAULT_NAMESPACE = "ribbon";
|
||||
|
||||
private EurekaClientConfig clientConfig;
|
||||
private SpringClientFactory clientFactory;
|
||||
private EurekaClientConfig clientConfig;
|
||||
|
||||
public EurekaRibbonClientPreprocessor(EurekaClientConfig clientConfig, SpringClientFactory clientFactory) {
|
||||
public EurekaRibbonClientPreprocessor(EurekaClientConfig clientConfig) {
|
||||
this.clientConfig = clientConfig;
|
||||
this.clientFactory = clientFactory;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preprocess(String serviceId) {
|
||||
@@ -67,40 +60,23 @@ public class EurekaRibbonClientPreprocessor implements RibbonClientPreprocessor
|
||||
setProp(serviceId, NIWSServerListFilterClassName.key(),
|
||||
ZonePreferenceServerListFilter.class.getName());
|
||||
setProp(serviceId, EnableZoneAffinity.key(), "true");
|
||||
ILoadBalancer loadBalancer = clientFactory.getNamedLoadBalancer(serviceId);
|
||||
wrapServerList(loadBalancer);
|
||||
}
|
||||
|
||||
private void wrapServerList(ILoadBalancer balancer) {
|
||||
if (balancer instanceof DynamicServerListLoadBalancer) {
|
||||
@SuppressWarnings("unchecked")
|
||||
DynamicServerListLoadBalancer<Server> dynamic = (DynamicServerListLoadBalancer<Server>) balancer;
|
||||
ServerList<Server> list = dynamic.getServerListImpl();
|
||||
if (!(list instanceof DomainExtractingServerList)) {
|
||||
// This is optional: you can use the native Eureka AWS features as long as
|
||||
// the server zone is populated. TODO: find a way to back off if AWS
|
||||
// metadata *is* available.
|
||||
// @see com.netflix.appinfo.AmazonInfo.Builder
|
||||
dynamic.setServerListImpl(new DomainExtractingServerList(list, dynamic.getClientConfig()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void setProp(String serviceId, String suffix, String value) {
|
||||
// how to set the namespace properly?
|
||||
String key = getKey(serviceId, suffix);
|
||||
DynamicStringProperty property = getProperty(key);
|
||||
if (property.get().equals(VALUE_NOT_SET)) {
|
||||
ConfigurationManager.getConfigInstance().setProperty(key, value);
|
||||
}
|
||||
String key = getKey(serviceId, suffix);
|
||||
DynamicStringProperty property = getProperty(key);
|
||||
if (property.get().equals(VALUE_NOT_SET)) {
|
||||
ConfigurationManager.getConfigInstance().setProperty(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
protected DynamicStringProperty getProperty(String key) {
|
||||
return DynamicPropertyFactory.getInstance().getStringProperty(key, VALUE_NOT_SET);
|
||||
}
|
||||
protected DynamicStringProperty getProperty(String key) {
|
||||
return DynamicPropertyFactory.getInstance().getStringProperty(key, VALUE_NOT_SET);
|
||||
}
|
||||
|
||||
protected String getKey(String serviceId, String suffix) {
|
||||
return serviceId + "." + DEFAULT_NAMESPACE + "." + suffix;
|
||||
}
|
||||
protected String getKey(String serviceId, String suffix) {
|
||||
return serviceId + "." + DEFAULT_NAMESPACE + "." + suffix;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -21,9 +21,9 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.cloud.netflix.ribbon.SpringClientFactory;
|
||||
import org.springframework.cloud.netflix.ribbon.RibbonAutoConfiguration;
|
||||
import org.springframework.cloud.netflix.ribbon.RibbonClientPreprocessor;
|
||||
import org.springframework.cloud.netflix.ribbon.SpringClientFactory;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
@@ -46,7 +46,7 @@ public class RibbonEurekaAutoConfiguration {
|
||||
private EurekaClientConfig clientConfig;
|
||||
|
||||
@Bean
|
||||
public RibbonClientPreprocessor ribbonClientPreprocessor(SpringClientFactory clientFactory) {
|
||||
return new EurekaRibbonClientPreprocessor(clientConfig, clientFactory);
|
||||
public RibbonClientPreprocessor ribbonClientPreprocessor() {
|
||||
return new EurekaRibbonClientPreprocessor(clientConfig);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,9 @@ package org.springframework.cloud.netflix.ribbon.eureka;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import com.netflix.client.config.IClientConfig;
|
||||
import com.netflix.config.ConfigurationManager;
|
||||
import com.netflix.config.DeploymentContext.ContextKey;
|
||||
@@ -31,6 +34,8 @@ import com.netflix.loadbalancer.ZoneAffinityServerListFilter;
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper=false)
|
||||
public class ZonePreferenceServerListFilter extends ZoneAffinityServerListFilter<Server> {
|
||||
|
||||
private String zone;
|
||||
|
||||
@@ -81,9 +81,7 @@ public class RibbonRoutingFilter extends ZuulFilter {
|
||||
|
||||
String serviceId = (String) context.get("serviceId");
|
||||
|
||||
preprocessor.preprocess(serviceId);
|
||||
|
||||
RestClient restClient = clientFactory.namedClient(serviceId, RestClient.class);
|
||||
RestClient restClient = clientFactory.getClient(serviceId, RestClient.class);
|
||||
|
||||
String uri = request.getRequestURI();
|
||||
if (context.get("requestURI") != null) {
|
||||
|
||||
@@ -15,6 +15,9 @@
|
||||
*/
|
||||
package org.springframework.cloud.netflix.eureka;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.springframework.boot.test.EnvironmentTestUtils.addEnvironment;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Test;
|
||||
import org.springframework.beans.factory.BeanCreationException;
|
||||
@@ -22,13 +25,9 @@ import org.springframework.boot.autoconfigure.PropertyPlaceholderAutoConfigurati
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.support.AbstractApplicationContext;
|
||||
|
||||
import com.netflix.appinfo.UniqueIdentifier;
|
||||
import com.netflix.appinfo.InstanceInfo.InstanceStatus;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import static org.springframework.boot.test.EnvironmentTestUtils.*;
|
||||
import com.netflix.appinfo.UniqueIdentifier;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
|
||||
@@ -4,6 +4,7 @@ import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.boot.test.IntegrationTest;
|
||||
import org.springframework.boot.test.SpringApplicationConfiguration;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.test.context.web.WebAppConfiguration;
|
||||
|
||||
@@ -11,6 +12,7 @@ import org.springframework.test.context.web.WebAppConfiguration;
|
||||
@SpringApplicationConfiguration(classes = Application.class)
|
||||
@WebAppConfiguration
|
||||
@IntegrationTest("server.port=0")
|
||||
@DirtiesContext
|
||||
public class ApplicationTests {
|
||||
|
||||
@Test
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
package org.springframework.cloud.netflix.feign;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
@@ -13,6 +13,7 @@ import org.springframework.boot.builder.SpringApplicationBuilder;
|
||||
import org.springframework.boot.test.IntegrationTest;
|
||||
import org.springframework.boot.test.SpringApplicationConfiguration;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.test.context.web.WebAppConfiguration;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
@@ -29,6 +30,7 @@ import java.util.List;
|
||||
@SpringApplicationConfiguration(classes = SpringDecoderTests.Application.class)
|
||||
@WebAppConfiguration
|
||||
@IntegrationTest({ "server.port=0", "spring.application.name=springdecodertest", "spring.jmx.enabled=true" })
|
||||
@DirtiesContext
|
||||
public class SpringDecoderTests extends FeignConfiguration {
|
||||
|
||||
@Value("${local.server.port}")
|
||||
|
||||
@@ -26,7 +26,7 @@ import static org.mockito.Mockito.*;
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
public class RibbonInterceptorTest {
|
||||
public class RibbonInterceptorTests {
|
||||
|
||||
@Mock
|
||||
HttpRequest request;
|
||||
@@ -1,28 +1,34 @@
|
||||
package org.springframework.cloud.netflix.ribbon;
|
||||
|
||||
import com.netflix.loadbalancer.BaseLoadBalancer;
|
||||
import com.netflix.loadbalancer.LoadBalancerStats;
|
||||
import com.netflix.loadbalancer.Server;
|
||||
import com.netflix.loadbalancer.ServerStats;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.fail;
|
||||
import static org.mockito.Matchers.anyDouble;
|
||||
import static org.mockito.Matchers.anyString;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.net.URI;
|
||||
import java.net.URL;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.Mockito;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
import org.springframework.cloud.client.ServiceInstance;
|
||||
import org.springframework.cloud.client.loadbalancer.LoadBalancerRequest;
|
||||
import org.springframework.cloud.netflix.ribbon.RibbonLoadBalancerClient.RibbonServer;
|
||||
|
||||
import java.net.URI;
|
||||
import java.net.URL;
|
||||
import java.util.Arrays;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
import com.netflix.loadbalancer.BaseLoadBalancer;
|
||||
import com.netflix.loadbalancer.LoadBalancerStats;
|
||||
import com.netflix.loadbalancer.Server;
|
||||
import com.netflix.loadbalancer.ServerStats;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
public class RibbonLoadBalancerClientTest {
|
||||
public class RibbonLoadBalancerClientTests {
|
||||
|
||||
@Mock
|
||||
RibbonClientPreprocessor preprocessor;
|
||||
@@ -42,6 +48,7 @@ public class RibbonLoadBalancerClientTest {
|
||||
@Before
|
||||
public void init() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
Mockito.when(clientFactory.getLoadBalancerContext(anyString())).thenReturn(new RibbonLoadBalancerContext(loadBalancer));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -122,11 +129,12 @@ public class RibbonLoadBalancerClientTest {
|
||||
}
|
||||
|
||||
protected RibbonLoadBalancerClient getRibbonLoadBalancerClient(RibbonServer ribbonServer) {
|
||||
when(loadBalancer.getName()).thenReturn(ribbonServer.getServiceId());
|
||||
when(loadBalancer.getName()).thenReturn(ribbonServer.getServiceId());
|
||||
when(loadBalancer.chooseServer(anyString())).thenReturn(ribbonServer.server);
|
||||
when(loadBalancer.getLoadBalancerStats()).thenReturn(loadBalancerStats);
|
||||
when(loadBalancerStats.getSingleServerStat(ribbonServer.server)).thenReturn(serverStats);
|
||||
when(clientFactory.getLoadBalancer(loadBalancer.getName())).thenReturn(loadBalancer);
|
||||
|
||||
return new RibbonLoadBalancerClient(preprocessor, clientFactory, Arrays.asList(loadBalancer));
|
||||
return new RibbonLoadBalancerClient(clientFactory);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright 2013-2014 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.netflix.ribbon;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.boot.test.EnvironmentTestUtils;
|
||||
import org.springframework.cloud.netflix.archaius.ArchaiusAutoConfiguration;
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
|
||||
import com.netflix.client.DefaultLoadBalancerRetryHandler;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
public class SpringClientFactoryTests {
|
||||
|
||||
private SpringClientFactory factory = new SpringClientFactory();
|
||||
|
||||
@Test
|
||||
public void testConfigureRetry() {
|
||||
AnnotationConfigApplicationContext parent = new AnnotationConfigApplicationContext(ArchaiusAutoConfiguration.class);
|
||||
EnvironmentTestUtils.addEnvironment(parent, "foo.ribbon.MaxAutoRetries:2");
|
||||
factory.setApplicationContext(parent);
|
||||
DefaultLoadBalancerRetryHandler retryHandler = (DefaultLoadBalancerRetryHandler) factory
|
||||
.getLoadBalancerContext("foo").getRetryHandler();
|
||||
assertEquals(2, retryHandler.getMaxRetriesOnSameServer());
|
||||
parent.close();
|
||||
factory.destroy();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,5 +1,16 @@
|
||||
package org.springframework.cloud.netflix.ribbon.eureka;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.netflix.appinfo.InstanceInfo;
|
||||
import com.netflix.client.config.CommonClientConfigKey;
|
||||
@@ -7,14 +18,6 @@ import com.netflix.client.config.DefaultClientConfigImpl;
|
||||
import com.netflix.loadbalancer.Server;
|
||||
import com.netflix.loadbalancer.ServerList;
|
||||
import com.netflix.niws.loadbalancer.DiscoveryEnabledServer;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
@@ -64,7 +67,8 @@ public class DomainExtractingServerListTests {
|
||||
|
||||
protected DomainExtractingServerList getDomainExtractingServerList(DefaultClientConfigImpl config) {
|
||||
DiscoveryEnabledServer server = mock(DiscoveryEnabledServer.class);
|
||||
ServerList originalServerList = mock(ServerList.class);
|
||||
@SuppressWarnings("unchecked")
|
||||
ServerList<Server> originalServerList = mock(ServerList.class);
|
||||
InstanceInfo instanceInfo = mock(InstanceInfo.class);
|
||||
|
||||
when(server.getInstanceInfo()).thenReturn(instanceInfo);
|
||||
@@ -75,7 +79,7 @@ public class DomainExtractingServerListTests {
|
||||
when(instanceInfo.getIPAddr()).thenReturn(IP_ADDR);
|
||||
when(instanceInfo.getPort()).thenReturn(PORT);
|
||||
|
||||
when(originalServerList.getInitialListOfServers()).thenReturn(Arrays.asList(server));
|
||||
when(originalServerList.getInitialListOfServers()).thenReturn(Arrays.<Server>asList(server));
|
||||
|
||||
return new DomainExtractingServerList(originalServerList, config);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* Copyright 2013-2014 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.netflix.ribbon.eureka;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.PropertyPlaceholderAutoConfiguration;
|
||||
import org.springframework.boot.test.SpringApplicationConfiguration;
|
||||
import org.springframework.cloud.netflix.archaius.ArchaiusAutoConfiguration;
|
||||
import org.springframework.cloud.netflix.eureka.EurekaClientAutoConfiguration;
|
||||
import org.springframework.cloud.netflix.ribbon.EnableRibbonClient;
|
||||
import org.springframework.cloud.netflix.ribbon.RibbonAutoConfiguration;
|
||||
import org.springframework.cloud.netflix.ribbon.RibbonClient;
|
||||
import org.springframework.cloud.netflix.ribbon.SpringClientFactory;
|
||||
import org.springframework.cloud.netflix.ribbon.eureka.EurekaRibbonClientPreprocessorIntegrationTests.TestConfiguration;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
|
||||
import com.netflix.loadbalancer.Server;
|
||||
import com.netflix.loadbalancer.ZoneAvoidanceRule;
|
||||
import com.netflix.loadbalancer.ZoneAwareLoadBalancer;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@SpringApplicationConfiguration(classes = TestConfiguration.class)
|
||||
@DirtiesContext
|
||||
public class EurekaRibbonClientPreprocessorIntegrationTests {
|
||||
|
||||
@Autowired
|
||||
private SpringClientFactory factory;
|
||||
|
||||
@Test
|
||||
public void serverListIsWrapped() throws Exception {
|
||||
@SuppressWarnings("unchecked")
|
||||
ZoneAwareLoadBalancer<Server> loadBalancer = (ZoneAwareLoadBalancer<Server>) factory
|
||||
.getLoadBalancer("foo");
|
||||
DomainExtractingServerList.class.cast(loadBalancer.getServerListImpl());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void ruleDefaultsToZoneAvoidance() throws Exception {
|
||||
@SuppressWarnings("unchecked")
|
||||
ZoneAwareLoadBalancer<Server> loadBalancer = (ZoneAwareLoadBalancer<Server>) factory
|
||||
.getLoadBalancer("foo");
|
||||
ZoneAvoidanceRule.class.cast(loadBalancer.getRule());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void serverListFilterOverride() throws Exception {
|
||||
@SuppressWarnings("unchecked")
|
||||
ZoneAwareLoadBalancer<Server> loadBalancer = (ZoneAwareLoadBalancer<Server>) factory
|
||||
.getLoadBalancer("foo");
|
||||
assertEquals("myTestZone",
|
||||
ZonePreferenceServerListFilter.class.cast(loadBalancer.getFilter()).getZone());
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableRibbonClient(@RibbonClient(name = "foo", configuration = FooConfiguration.class))
|
||||
@Import({ PropertyPlaceholderAutoConfiguration.class,
|
||||
ArchaiusAutoConfiguration.class, EurekaClientAutoConfiguration.class,
|
||||
RibbonAutoConfiguration.class, RibbonEurekaAutoConfiguration.class })
|
||||
protected static class TestConfiguration {
|
||||
}
|
||||
|
||||
@Configuration
|
||||
protected static class FooConfiguration {
|
||||
@Bean
|
||||
public ZonePreferenceServerListFilter serverListFilter() {
|
||||
ZonePreferenceServerListFilter filter = new ZonePreferenceServerListFilter();
|
||||
filter.setZone("myTestZone");
|
||||
return filter;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -18,16 +18,16 @@ package org.springframework.cloud.netflix.ribbon.eureka;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.springframework.cloud.netflix.ribbon.eureka.EurekaRibbonClientPreprocessor.*;
|
||||
import static org.springframework.cloud.netflix.ribbon.eureka.EurekaRibbonClientPreprocessor.VALUE_NOT_SET;
|
||||
|
||||
import com.netflix.config.DynamicStringProperty;
|
||||
import org.junit.After;
|
||||
import org.junit.Test;
|
||||
import org.springframework.cloud.netflix.ribbon.SpringClientFactory;
|
||||
import org.springframework.cloud.netflix.eureka.EurekaClientConfigBean;
|
||||
import org.springframework.cloud.netflix.ribbon.SpringClientFactory;
|
||||
|
||||
import com.netflix.config.ConfigurationManager;
|
||||
import com.netflix.config.DeploymentContext.ContextKey;
|
||||
import com.netflix.config.DynamicStringProperty;
|
||||
import com.netflix.loadbalancer.ILoadBalancer;
|
||||
import com.netflix.loadbalancer.Server;
|
||||
import com.netflix.loadbalancer.ZoneAwareLoadBalancer;
|
||||
@@ -37,7 +37,7 @@ import com.netflix.loadbalancer.ZoneAwareLoadBalancer;
|
||||
*
|
||||
*/
|
||||
public class EurekaRibbonClientPreprocessorTests {
|
||||
|
||||
|
||||
@After
|
||||
public void close() {
|
||||
ConfigurationManager.getDeploymentContext().setValue(ContextKey.zone, "");
|
||||
@@ -47,40 +47,41 @@ public class EurekaRibbonClientPreprocessorTests {
|
||||
public void basicConfigurationCreatedForLoadBalancer() {
|
||||
EurekaClientConfigBean client = new EurekaClientConfigBean();
|
||||
client.getAvailabilityZones().put(client.getRegion(), "foo");
|
||||
SpringClientFactory clientFactory = new SpringClientFactory();
|
||||
EurekaRibbonClientPreprocessor clientPreprocessor = new EurekaRibbonClientPreprocessor(
|
||||
client, clientFactory);
|
||||
SpringClientFactory clientFactory = new SpringClientFactory();
|
||||
EurekaRibbonClientPreprocessor clientPreprocessor = new EurekaRibbonClientPreprocessor(
|
||||
client);
|
||||
clientPreprocessor.preprocess("service");
|
||||
ILoadBalancer balancer = clientFactory.getNamedLoadBalancer("service");
|
||||
ILoadBalancer balancer = clientFactory.getLoadBalancer("service");
|
||||
assertNotNull(balancer);
|
||||
@SuppressWarnings("unchecked")
|
||||
ZoneAwareLoadBalancer<Server> aware = (ZoneAwareLoadBalancer<Server>) balancer;
|
||||
assertTrue(aware.getServerListImpl() instanceof DomainExtractingServerList);
|
||||
assertEquals("foo", ConfigurationManager.getDeploymentContext().getValue(ContextKey.zone));
|
||||
assertEquals("foo",
|
||||
ConfigurationManager.getDeploymentContext().getValue(ContextKey.zone));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetProp() {
|
||||
EurekaClientConfigBean client = new EurekaClientConfigBean();
|
||||
SpringClientFactory clientFactory = new SpringClientFactory();
|
||||
EurekaRibbonClientPreprocessor preprocessor = new EurekaRibbonClientPreprocessor(
|
||||
client, clientFactory);
|
||||
@Test
|
||||
public void testSetProp() {
|
||||
EurekaClientConfigBean client = new EurekaClientConfigBean();
|
||||
EurekaRibbonClientPreprocessor preprocessor = new EurekaRibbonClientPreprocessor(
|
||||
client);
|
||||
|
||||
String serviceId = "myService";
|
||||
String suffix = "mySuffix";
|
||||
String value = "myValue";
|
||||
String serviceId = "myService";
|
||||
String suffix = "mySuffix";
|
||||
String value = "myValue";
|
||||
|
||||
DynamicStringProperty property = preprocessor.getProperty(preprocessor.getKey(serviceId, suffix));
|
||||
DynamicStringProperty property = preprocessor.getProperty(preprocessor.getKey(
|
||||
serviceId, suffix));
|
||||
|
||||
assertEquals("property doesn't have default value", VALUE_NOT_SET, property.get());
|
||||
assertEquals("property doesn't have default value", VALUE_NOT_SET, property.get());
|
||||
|
||||
preprocessor.setProp(serviceId, suffix, value);
|
||||
preprocessor.setProp(serviceId, suffix, value);
|
||||
|
||||
assertEquals("property has wrong value", value, property.get());
|
||||
assertEquals("property has wrong value", value, property.get());
|
||||
|
||||
preprocessor.setProp(serviceId, suffix, value);
|
||||
preprocessor.setProp(serviceId, suffix, value);
|
||||
|
||||
assertEquals("property has wrong value", value, property.get());
|
||||
}
|
||||
assertEquals("property has wrong value", value, property.get());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ import org.springframework.http.HttpEntity;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.test.context.web.WebAppConfiguration;
|
||||
|
||||
@@ -22,6 +23,7 @@ import org.springframework.test.context.web.WebAppConfiguration;
|
||||
@IntegrationTest({ "server.port: 0",
|
||||
"zuul.routes.other: /test/**=http://localhost:7777/local",
|
||||
"zuul.routes.simple: /simple/**" })
|
||||
@DirtiesContext
|
||||
public class SampleZuulProxyApplicationTests {
|
||||
|
||||
@Value("${local.server.port}")
|
||||
|
||||
Reference in New Issue
Block a user