Commit 3ed982d2 by Spencer Gibb

Introduce Ribbon ServerIntrospector.

Allows ribbon load balancer implementations to answer questions like isSecure(Server).
parent a38ba340
/*
* 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.netflix.ribbon;
import com.netflix.loadbalancer.Server;
/**
* @author Spencer Gibb
*/
public class DefaultServerIntrospector implements ServerIntrospector {
@Override
public boolean isSecure(Server server) {
// Can we do better?
return (""+server.getPort()).endsWith("443");
}
}
...@@ -26,10 +26,8 @@ import org.springframework.boot.context.properties.EnableConfigurationProperties ...@@ -26,10 +26,8 @@ import org.springframework.boot.context.properties.EnableConfigurationProperties
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy; import org.springframework.context.annotation.Lazy;
import org.springframework.util.ClassUtils;
import org.springframework.web.util.UriComponentsBuilder; import org.springframework.web.util.UriComponentsBuilder;
import com.netflix.appinfo.InstanceInfo.PortType;
import com.netflix.client.config.DefaultClientConfigImpl; import com.netflix.client.config.DefaultClientConfigImpl;
import com.netflix.client.config.IClientConfig; import com.netflix.client.config.IClientConfig;
import com.netflix.loadbalancer.ConfigurationBasedServerList; import com.netflix.loadbalancer.ConfigurationBasedServerList;
...@@ -44,7 +42,6 @@ import com.netflix.loadbalancer.ServerListFilter; ...@@ -44,7 +42,6 @@ import com.netflix.loadbalancer.ServerListFilter;
import com.netflix.loadbalancer.ZoneAvoidanceRule; import com.netflix.loadbalancer.ZoneAvoidanceRule;
import com.netflix.loadbalancer.ZoneAwareLoadBalancer; import com.netflix.loadbalancer.ZoneAwareLoadBalancer;
import com.netflix.niws.client.http.RestClient; import com.netflix.niws.client.http.RestClient;
import com.netflix.niws.loadbalancer.DiscoveryEnabledServer;
import com.netflix.servo.monitor.Monitors; import com.netflix.servo.monitor.Monitors;
import com.sun.jersey.api.client.Client; import com.sun.jersey.api.client.Client;
import com.sun.jersey.client.apache4.ApacheHttpClient4; import com.sun.jersey.client.apache4.ApacheHttpClient4;
...@@ -95,9 +92,9 @@ public class RibbonClientConfiguration { ...@@ -95,9 +92,9 @@ public class RibbonClientConfiguration {
} }
/** /**
* Create a Netflix {@link RestClient} integrated with Ribbon if none already exists in the * Create a Netflix {@link RestClient} integrated with Ribbon if none already exists
* application context. It is not required for Ribbon to work properly and is therefore * in the application context. It is not required for Ribbon to work properly and is
* created lazily if ever another component requires it. * therefore created lazily if ever another component requires it.
* *
* @param config the configuration to use by the underlying Ribbon instance * @param config the configuration to use by the underlying Ribbon instance
* @param loadBalancer the load balancer to use by the underlying Ribbon instance * @param loadBalancer the load balancer to use by the underlying Ribbon instance
...@@ -106,8 +103,9 @@ public class RibbonClientConfiguration { ...@@ -106,8 +103,9 @@ public class RibbonClientConfiguration {
@Bean @Bean
@Lazy @Lazy
@ConditionalOnMissingBean @ConditionalOnMissingBean
public RestClient ribbonRestClient(IClientConfig config, ILoadBalancer loadBalancer) { public RestClient ribbonRestClient(IClientConfig config, ILoadBalancer loadBalancer,
RestClient client = new OverrideRestClient(config); ServerIntrospector serverIntrospector) {
RestClient client = new OverrideRestClient(config, serverIntrospector);
client.setLoadBalancer(loadBalancer); client.setLoadBalancer(loadBalancer);
Monitors.registerObject("Client_" + this.name, client); Monitors.registerObject("Client_" + this.name, client);
return client; return client;
...@@ -140,34 +138,33 @@ public class RibbonClientConfiguration { ...@@ -140,34 +138,33 @@ public class RibbonClientConfiguration {
return new RibbonLoadBalancerContext(loadBalancer, config); return new RibbonLoadBalancerContext(loadBalancer, config);
} }
@Bean
@ConditionalOnMissingBean
public ServerIntrospector serverIntrospector() {
return new DefaultServerIntrospector();
}
static class OverrideRestClient extends RestClient { static class OverrideRestClient extends RestClient {
protected OverrideRestClient(IClientConfig ncc) { private ServerIntrospector serverIntrospector;
protected OverrideRestClient(IClientConfig ncc,
ServerIntrospector serverIntrospector) {
super(); super();
this.serverIntrospector = serverIntrospector;
initWithNiwsConfig(ncc); initWithNiwsConfig(ncc);
} }
@Override @Override
public URI reconstructURIWithServer(Server server, URI original) { public URI reconstructURIWithServer(Server server, URI original) {
String scheme = original.getScheme(); String scheme = original.getScheme();
if (!"https".equals(scheme) && isSecure(server)) { if (!"https".equals(scheme) && serverIntrospector.isSecure(server)) {
original = UriComponentsBuilder.fromUri(original).scheme("https").build().toUri(); original = UriComponentsBuilder.fromUri(original).scheme("https").build()
.toUri();
} }
return super.reconstructURIWithServer(server, original); return super.reconstructURIWithServer(server, original);
} }
private boolean isSecure(Server server) {
if (ClassUtils.isPresent("com.netflix.niws.loadbalancer.DiscoveryEnabledServer",
null)) {
if (server instanceof DiscoveryEnabledServer) {
DiscoveryEnabledServer enabled = (DiscoveryEnabledServer) server;
return enabled.getInstanceInfo().isPortEnabled(PortType.SECURE);
}
}
// Can we do better?
return (""+server.getPort()).endsWith("443");
}
@Override @Override
protected Client apacheHttpClientSpecificInitialization() { protected Client apacheHttpClientSpecificInitialization() {
ApacheHttpClient4 apache = (ApacheHttpClient4) super ApacheHttpClient4 apache = (ApacheHttpClient4) super
......
/*
* 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.netflix.ribbon;
import com.netflix.loadbalancer.Server;
/**
* @author Spencer Gibb
*/
public interface ServerIntrospector {
boolean isSecure(Server server);
}
...@@ -177,7 +177,7 @@ public class SpringClientFactory implements DisposableBean, ApplicationContextAw ...@@ -177,7 +177,7 @@ public class SpringClientFactory implements DisposableBean, ApplicationContextAw
return result; return result;
} }
private <C> C getInstance(String name, Class<C> type) { public <C> C getInstance(String name, Class<C> type) {
AnnotationConfigApplicationContext context = getContext(name); AnnotationConfigApplicationContext context = getContext(name);
if (BeanFactoryUtils.beanNamesForTypeIncludingAncestors(context, type).length > 0) { if (BeanFactoryUtils.beanNamesForTypeIncludingAncestors(context, type).length > 0) {
return context.getBean(type); return context.getBean(type);
......
...@@ -27,6 +27,7 @@ import org.springframework.beans.factory.annotation.Autowired; ...@@ -27,6 +27,7 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.cloud.netflix.eureka.EurekaInstanceConfigBean; import org.springframework.cloud.netflix.eureka.EurekaInstanceConfigBean;
import org.springframework.cloud.netflix.ribbon.ServerIntrospector;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
...@@ -101,6 +102,11 @@ public class EurekaRibbonClientConfiguration { ...@@ -101,6 +102,11 @@ public class EurekaRibbonClientConfiguration {
return serverList; return serverList;
} }
@Bean
public ServerIntrospector serverIntrospector() {
return new EurekaServerIntrospector();
}
@PostConstruct @PostConstruct
public void preprocess() { public void preprocess() {
String zone = ConfigurationManager.getDeploymentContext().getValue( String zone = ConfigurationManager.getDeploymentContext().getValue(
......
/*
* 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.netflix.ribbon.eureka;
import com.netflix.appinfo.InstanceInfo;
import com.netflix.loadbalancer.Server;
import com.netflix.niws.loadbalancer.DiscoveryEnabledServer;
import org.springframework.cloud.netflix.ribbon.DefaultServerIntrospector;
/**
* @author Spencer Gibb
*/
public class EurekaServerIntrospector extends DefaultServerIntrospector {
@Override
public boolean isSecure(Server server) {
if (server instanceof DiscoveryEnabledServer) {
DiscoveryEnabledServer enabled = (DiscoveryEnabledServer) server;
return enabled.getInstanceInfo().isPortEnabled(InstanceInfo.PortType.SECURE);
}
return super.isSecure(server);
}
}
...@@ -47,7 +47,7 @@ public class RibbonClientConfigurationTests { ...@@ -47,7 +47,7 @@ public class RibbonClientConfigurationTests {
static class TestRestClient extends RibbonClientConfiguration.OverrideRestClient { static class TestRestClient extends RibbonClientConfiguration.OverrideRestClient {
private TestRestClient(IClientConfig ncc) { private TestRestClient(IClientConfig ncc) {
super(ncc); super(ncc, new DefaultServerIntrospector());
} }
@Override @Override
......
...@@ -25,6 +25,7 @@ import org.springframework.cloud.netflix.archaius.ArchaiusAutoConfiguration; ...@@ -25,6 +25,7 @@ import org.springframework.cloud.netflix.archaius.ArchaiusAutoConfiguration;
import org.springframework.cloud.netflix.eureka.EurekaClientAutoConfiguration; import org.springframework.cloud.netflix.eureka.EurekaClientAutoConfiguration;
import org.springframework.cloud.netflix.ribbon.RibbonAutoConfiguration; import org.springframework.cloud.netflix.ribbon.RibbonAutoConfiguration;
import org.springframework.cloud.netflix.ribbon.RibbonClient; import org.springframework.cloud.netflix.ribbon.RibbonClient;
import org.springframework.cloud.netflix.ribbon.ServerIntrospector;
import org.springframework.cloud.netflix.ribbon.SpringClientFactory; import org.springframework.cloud.netflix.ribbon.SpringClientFactory;
import org.springframework.cloud.netflix.ribbon.eureka.EurekaRibbonClientPreprocessorIntegrationTests.TestConfiguration; import org.springframework.cloud.netflix.ribbon.eureka.EurekaRibbonClientPreprocessorIntegrationTests.TestConfiguration;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
...@@ -63,6 +64,11 @@ public class EurekaRibbonClientPreprocessorIntegrationTests { ...@@ -63,6 +64,11 @@ public class EurekaRibbonClientPreprocessorIntegrationTests {
NIWSDiscoveryPing.class.cast(getLoadBalancer().getPing()); NIWSDiscoveryPing.class.cast(getLoadBalancer().getPing());
} }
@Test
public void serverIntrospectorDefaultsToEureka() throws Exception {
EurekaServerIntrospector.class.cast(this.factory.getInstance("foo", ServerIntrospector.class));
}
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
private ZoneAwareLoadBalancer<Server> getLoadBalancer() { private ZoneAwareLoadBalancer<Server> getLoadBalancer() {
return (ZoneAwareLoadBalancer<Server>) this.factory return (ZoneAwareLoadBalancer<Server>) this.factory
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment