GH-1334: Support for eagerly initializing zuul Ribbon named client contexts

This commit is contained in:
Biju Kunjummen
2017-03-02 16:46:01 -08:00
parent 66163bee2b
commit a6fe648a41
6 changed files with 182 additions and 9 deletions

View File

@@ -0,0 +1,38 @@
package org.springframework.cloud.netflix.ribbon;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.ApplicationListener;
import java.util.List;
/**
* Responsible for eagerly creating the child application context holding the Ribbon
* related configuration
*
* @author Biju Kunjummen
*/
public class RibbonApplicationContextInitializer
implements ApplicationListener<ApplicationReadyEvent> {
private final SpringClientFactory springClientFactory;
private final List<String> serviceIds;
public RibbonApplicationContextInitializer(SpringClientFactory springClientFactory,
List<String> serviceIds) {
this.springClientFactory = springClientFactory;
this.serviceIds = serviceIds;
}
private void initialize() {
if (serviceIds != null) {
for (String serviceId : serviceIds) {
this.springClientFactory.getContext(serviceId);
}
}
}
// @Override
public void onApplicationEvent(ApplicationReadyEvent event) {
initialize();
}
}

View File

@@ -80,28 +80,29 @@ public class SpringClientFactory extends NamedContextFactory<RibbonClientSpecifi
}
static <C> C instantiateWithConfig(AnnotationConfigApplicationContext context,
Class<C> clazz, IClientConfig config) {
Class<C> clazz, IClientConfig config) {
C result = null;
try {
Constructor<C> constructor = clazz.getConstructor(IClientConfig.class);
result = constructor.newInstance(config);
} catch (Throwable e) {
}
catch (Throwable e) {
// Ignored
}
if (result == null) {
result = BeanUtils.instantiate(clazz);
if (result instanceof IClientConfigAware) {
((IClientConfigAware) result).initWithNiwsConfig(config);
}
if (context != null) {
context.getAutowireCapableBeanFactory().autowireBean(result);
}
}
return result;
}
@@ -115,5 +116,8 @@ public class SpringClientFactory extends NamedContextFactory<RibbonClientSpecifi
return instantiateWithConfig(getContext(name), type, config);
}
@Override
protected AnnotationConfigApplicationContext getContext(String name) {
return super.getContext(name);
}
}

View File

@@ -22,6 +22,7 @@ import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.web.ErrorController;
import org.springframework.boot.autoconfigure.web.ServerProperties;
import org.springframework.boot.autoconfigure.web.ServerPropertiesAutoConfiguration;
@@ -31,6 +32,7 @@ import org.springframework.cloud.client.actuator.HasFeatures;
import org.springframework.cloud.client.discovery.event.HeartbeatEvent;
import org.springframework.cloud.client.discovery.event.HeartbeatMonitor;
import org.springframework.cloud.context.scope.refresh.RefreshScopeRefreshedEvent;
import org.springframework.cloud.netflix.ribbon.SpringClientFactory;
import org.springframework.cloud.netflix.zuul.filters.CompositeRouteLocator;
import org.springframework.cloud.netflix.zuul.filters.RouteLocator;
import org.springframework.cloud.netflix.zuul.filters.SimpleRouteLocator;
@@ -161,6 +163,14 @@ public class ZuulConfiguration {
return new SendForwardFilter();
}
@Bean
@ConditionalOnProperty(value = "zuul.context.startup.enabled", matchIfMissing = false)
public ZuulRouteApplicationContextInitializer zuulRoutesApplicationContextInitiazer(
SpringClientFactory springClientFactory) {
return new ZuulRouteApplicationContextInitializer(springClientFactory,
zuulProperties);
}
@Configuration
protected static class ZuulFilterConfiguration {

View File

@@ -0,0 +1,41 @@
package org.springframework.cloud.netflix.zuul;
import org.springframework.cloud.netflix.ribbon.RibbonApplicationContextInitializer;
import org.springframework.cloud.netflix.ribbon.SpringClientFactory;
import org.springframework.cloud.netflix.zuul.filters.ZuulProperties;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
/**
* Responsible for taking in the list of registered serviceid's (Ribbon client names)
* and creating the Spring {@link org.springframework.context.ApplicationContext} on
* start-up
*
* @author Biju Kunjummen
*/
public class ZuulRouteApplicationContextInitializer extends
RibbonApplicationContextInitializer {
public ZuulRouteApplicationContextInitializer(SpringClientFactory springClientFactory,
ZuulProperties zuulProperties) {
super(springClientFactory, getServiceIdsFromZuulProps(zuulProperties));
}
private static List<String> getServiceIdsFromZuulProps(ZuulProperties zuulProperties) {
Map<String, ZuulProperties.ZuulRoute> zuulRoutes = zuulProperties.getRoutes();
Collection<ZuulProperties.ZuulRoute> registeredRoutes = zuulRoutes.values();
List<String> serviceIds = new ArrayList<>();
if (registeredRoutes != null) {
for (ZuulProperties.ZuulRoute route: registeredRoutes) {
String serviceId = route.getServiceId();
if (serviceId != null) {
serviceIds.add(serviceId);
}
}
}
return serviceIds;
}
}

View File

@@ -0,0 +1,80 @@
package org.springframework.cloud.netflix.ribbon;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.netflix.archaius.ArchaiusAutoConfiguration;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.junit4.SpringRunner;
import java.util.Arrays;
import java.util.concurrent.atomic.AtomicInteger;
import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.boot.test.util.EnvironmentTestUtils.addEnvironment;
/**
* @author Biju Kunjummen
*/
@RunWith(SpringRunner.class)
@SpringBootTest(classes = {RibbonAutoConfiguration.class,
ArchaiusAutoConfiguration.class, RibbonApplicationContextInitializerTests.RibbonInitializerConfig.class})
@DirtiesContext
public class RibbonApplicationContextInitializerTests {
@Autowired
private SpringClientFactory springClientFactory;
@Test
public void testContextShouldInitalizeChildContexts() {
// Context should have been initialized and an instance of Foo created
assertThat(Foo.getInstanceCount()).isEqualTo(1);
ApplicationContext ctx = springClientFactory.getContext("testspec");
assertThat(Foo.getInstanceCount()).isEqualTo(1);
Foo foo = ctx.getBean("foo", Foo.class);
assertThat(foo).isNotNull();
}
@Configuration
static class FooConfig {
@Bean
public Foo foo() {
return new Foo();
}
}
@Configuration
@RibbonClient(name="testspec", configuration = FooConfig.class)
static class RibbonInitializerConfig {
@Bean
public RibbonApplicationContextInitializer ribbonApplicationContextInitializer(
SpringClientFactory springClientFactory) {
return new RibbonApplicationContextInitializer(springClientFactory,
Arrays.asList("testspec"));
}
}
static class Foo {
private static final AtomicInteger INSTANCE_COUNT = new AtomicInteger();
public Foo() {
INSTANCE_COUNT.incrementAndGet();
}
public static int getInstanceCount() {
return INSTANCE_COUNT.get();
}
}
}

View File

@@ -79,7 +79,7 @@ import com.netflix.loadbalancer.ServerList;
"zuul.routes.other: /test/**=http://localhost:7777/local",
"zuul.routes.another: /another/twolevel/**", "zuul.routes.simple: /simple/**",
"zuul.routes.singleton: /singleton/**",
"zuul.routes.singleton.sensitiveHeaders: " })
"zuul.routes.singleton.sensitiveHeaders: ","zuul.context.startup.enabled: true" })
@DirtiesContext
public class HttpClientRibbonCommandIntegrationTests extends ZuulProxyTestBase {