GH-1334: Support for eagerly initializing zuul Ribbon named client contexts
This commit is contained in:
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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 {
|
||||
|
||||
|
||||
Reference in New Issue
Block a user