From a6fe648a41ecf2b8d26311aeaeef4ab1272ce06a Mon Sep 17 00:00:00 2001 From: Biju Kunjummen Date: Thu, 2 Mar 2017 16:46:01 -0800 Subject: [PATCH 1/5] GH-1334: Support for eagerly initializing zuul Ribbon named client contexts --- .../RibbonApplicationContextInitializer.java | 38 +++++++++ .../netflix/ribbon/SpringClientFactory.java | 20 +++-- .../cloud/netflix/zuul/ZuulConfiguration.java | 10 +++ ...uulRouteApplicationContextInitializer.java | 41 ++++++++++ ...bonApplicationContextInitializerTests.java | 80 +++++++++++++++++++ ...tpClientRibbonCommandIntegrationTests.java | 2 +- 6 files changed, 182 insertions(+), 9 deletions(-) create mode 100644 spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/RibbonApplicationContextInitializer.java create mode 100644 spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/zuul/ZuulRouteApplicationContextInitializer.java create mode 100644 spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/ribbon/RibbonApplicationContextInitializerTests.java diff --git a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/RibbonApplicationContextInitializer.java b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/RibbonApplicationContextInitializer.java new file mode 100644 index 00000000..ce8782b9 --- /dev/null +++ b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/RibbonApplicationContextInitializer.java @@ -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 { + + private final SpringClientFactory springClientFactory; + private final List serviceIds; + + public RibbonApplicationContextInitializer(SpringClientFactory springClientFactory, + List 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(); + } +} diff --git a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/SpringClientFactory.java b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/SpringClientFactory.java index 7dbccf5d..f3ebd424 100644 --- a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/SpringClientFactory.java +++ b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/SpringClientFactory.java @@ -80,28 +80,29 @@ public class SpringClientFactory extends NamedContextFactory C instantiateWithConfig(AnnotationConfigApplicationContext context, - Class clazz, IClientConfig config) { + Class clazz, IClientConfig config) { C result = null; - + try { Constructor 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 getServiceIdsFromZuulProps(ZuulProperties zuulProperties) { + Map zuulRoutes = zuulProperties.getRoutes(); + Collection registeredRoutes = zuulRoutes.values(); + List serviceIds = new ArrayList<>(); + if (registeredRoutes != null) { + for (ZuulProperties.ZuulRoute route: registeredRoutes) { + String serviceId = route.getServiceId(); + if (serviceId != null) { + serviceIds.add(serviceId); + } + } + } + return serviceIds; + } +} diff --git a/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/ribbon/RibbonApplicationContextInitializerTests.java b/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/ribbon/RibbonApplicationContextInitializerTests.java new file mode 100644 index 00000000..9b34ad60 --- /dev/null +++ b/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/ribbon/RibbonApplicationContextInitializerTests.java @@ -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(); + } + } +} diff --git a/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/zuul/filters/route/apache/HttpClientRibbonCommandIntegrationTests.java b/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/zuul/filters/route/apache/HttpClientRibbonCommandIntegrationTests.java index 6f420150..14f07e2b 100644 --- a/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/zuul/filters/route/apache/HttpClientRibbonCommandIntegrationTests.java +++ b/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/zuul/filters/route/apache/HttpClientRibbonCommandIntegrationTests.java @@ -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 { From 31b617b10faf1f6dfb74221502b4cfd9216a658c Mon Sep 17 00:00:00 2001 From: Biju Kunjummen Date: Mon, 6 Mar 2017 09:30:42 -0800 Subject: [PATCH 2/5] Fixed formatting issues, added more tests --- .../netflix/ribbon/SpringClientFactory.java | 17 +-- .../cloud/netflix/zuul/ZuulConfiguration.java | 2 +- ...bonApplicationContextInitializerTests.java | 3 - .../EagerLoadOfZuulConfigurationTests.java | 59 ++++++++++ .../LazyLoadOfZuulConfigurationTests.java | 101 ++++++++++++++++++ 5 files changed, 170 insertions(+), 12 deletions(-) create mode 100644 spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/zuul/filters/route/EagerLoadOfZuulConfigurationTests.java create mode 100644 spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/zuul/filters/route/LazyLoadOfZuulConfigurationTests.java diff --git a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/SpringClientFactory.java b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/SpringClientFactory.java index f3ebd424..e63d49f0 100644 --- a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/SpringClientFactory.java +++ b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/SpringClientFactory.java @@ -80,29 +80,28 @@ public class SpringClientFactory extends NamedContextFactory C instantiateWithConfig(AnnotationConfigApplicationContext context, - Class clazz, IClientConfig config) { + Class clazz, IClientConfig config) { C result = null; - + try { Constructor 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; } @@ -120,4 +119,6 @@ public class SpringClientFactory extends NamedContextFactory result = new TestRestTemplate().getForEntity(uri, + String.class); + + // the instance should be available now.. + assertThat(Foo.getInstanceCount()).isEqualTo(1); + + assertThat(result.getStatusCode()).isEqualTo(HttpStatus.OK); + assertThat(result.getBody()).isEqualTo("sample"); + } + + @EnableAutoConfiguration + @Configuration + @EnableZuulProxy + @RibbonClients(@RibbonClient(name = "eager", configuration = FooConfig.class)) + static class TestConfig { + + } + + static class Foo { + private static final AtomicInteger INSTANCE_COUNT = new AtomicInteger(); + + public Foo() { + INSTANCE_COUNT.incrementAndGet(); + } + + public static int getInstanceCount() { + return INSTANCE_COUNT.get(); + } + } + + static class FooConfig { + + @Bean + public Foo foo() { + return new Foo(); + } + + @Value("${local.server.port}") + private int port; + + @Bean + public ServerList ribbonServerList() { + return new StaticServerList<>(new Server("localhost", this.port)); + } + + } + + @Configuration + @RestController + static class SampleWebConfig { + + @RequestMapping("/sample") + public String sampleEndpoint() { + return "sample"; + } + + } +} From 567a6c4a9f3a39ecfeb76d80f33c585c2ca1695a Mon Sep 17 00:00:00 2001 From: Biju Kunjummen Date: Tue, 7 Mar 2017 09:41:33 -0800 Subject: [PATCH 3/5] Support for eagerly loading Ribbon clients through configuration properties --- .../RibbonApplicationContextInitializer.java | 18 ++++- .../ribbon/RibbonAutoConfiguration.java | 15 +++- .../ribbon/RibbonEagerLoadProperties.java | 49 ++++++++++++ .../netflix/ribbon/SpringClientFactory.java | 2 +- .../cloud/netflix/zuul/ZuulConfiguration.java | 3 +- ...uulRouteApplicationContextInitializer.java | 16 ++++ ...bonApplicationContextInitializerTests.java | 16 ++++ .../RibbonClientsEagerInitializationTest.java | 79 +++++++++++++++++++ .../EagerLoadOfZuulConfigurationTests.java | 17 ++++ .../LazyLoadOfZuulConfigurationTests.java | 17 ++++ ...tpClientRibbonCommandIntegrationTests.java | 2 +- 11 files changed, 229 insertions(+), 5 deletions(-) create mode 100644 spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/RibbonEagerLoadProperties.java create mode 100644 spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/ribbon/RibbonClientsEagerInitializationTest.java diff --git a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/RibbonApplicationContextInitializer.java b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/RibbonApplicationContextInitializer.java index ce8782b9..3a300aad 100644 --- a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/RibbonApplicationContextInitializer.java +++ b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/RibbonApplicationContextInitializer.java @@ -1,3 +1,19 @@ +/* + * Copyright 2017 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 org.springframework.boot.context.event.ApplicationReadyEvent; @@ -31,7 +47,7 @@ public class RibbonApplicationContextInitializer } } -// @Override + @Override public void onApplicationEvent(ApplicationReadyEvent event) { initialize(); } diff --git a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/RibbonAutoConfiguration.java b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/RibbonAutoConfiguration.java index ca3a2714..99f67e2b 100644 --- a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/RibbonAutoConfiguration.java +++ b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/RibbonAutoConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2015 the original author or authors. + * Copyright 2013-2017 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. @@ -32,6 +32,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.cloud.client.actuator.HasFeatures; import org.springframework.cloud.client.loadbalancer.AsyncLoadBalancerAutoConfiguration; import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryPolicyFactory; @@ -53,16 +54,21 @@ import com.netflix.ribbon.Ribbon; * * @author Spencer Gibb * @author Dave Syer + * @author Biju Kunjummen */ @Configuration @ConditionalOnClass({ IClient.class, RestTemplate.class, AsyncRestTemplate.class, Ribbon.class}) @RibbonClients @AutoConfigureAfter(name = "org.springframework.cloud.netflix.eureka.EurekaClientAutoConfiguration") @AutoConfigureBefore({LoadBalancerAutoConfiguration.class, AsyncLoadBalancerAutoConfiguration.class}) +@EnableConfigurationProperties(RibbonEagerLoadProperties.class) public class RibbonAutoConfiguration { @Autowired(required = false) private List configurations = new ArrayList<>(); + + @Autowired + private RibbonEagerLoadProperties ribbonEagerLoadProperties; @Bean public HasFeatures ribbonFeature() { @@ -99,6 +105,13 @@ public class RibbonAutoConfiguration { public PropertiesFactory propertiesFactory() { return new PropertiesFactory(); } + + @Bean + @ConditionalOnProperty(value = "ribbon.eager-load.enabled", matchIfMissing = false) + public RibbonApplicationContextInitializer ribbonApplicationContextInitializer() { + return new RibbonApplicationContextInitializer(springClientFactory(), + ribbonEagerLoadProperties.getServiceIds()); + } @Configuration @ConditionalOnClass(HttpRequest.class) diff --git a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/RibbonEagerLoadProperties.java b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/RibbonEagerLoadProperties.java new file mode 100644 index 00000000..7b350929 --- /dev/null +++ b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/RibbonEagerLoadProperties.java @@ -0,0 +1,49 @@ +/* + * Copyright 2017 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 org.springframework.boot.context.properties.ConfigurationProperties; + +import java.util.List; + +/* + * Configuration Properties to indicate which Ribbon configurations + * should be eagerly loaded up + * + * @author Biju Kunjummen + */ +@ConfigurationProperties(prefix = "ribbon.eager-load") +public class RibbonEagerLoadProperties { + private boolean enabled = false; + private List serviceIds; + + public boolean isEnabled() { + return enabled; + } + + public void setEnabled(boolean enabled) { + this.enabled = enabled; + } + + public List getServiceIds() { + return serviceIds; + } + + public void setServiceIds(List serviceIds) { + this.serviceIds = serviceIds; + } +} diff --git a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/SpringClientFactory.java b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/SpringClientFactory.java index e63d49f0..1f9dcdda 100644 --- a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/SpringClientFactory.java +++ b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/SpringClientFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2015 the original author or authors. + * Copyright 2013-2017 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. diff --git a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/zuul/ZuulConfiguration.java b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/zuul/ZuulConfiguration.java index 920fd69d..7bab803c 100644 --- a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/zuul/ZuulConfiguration.java +++ b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/zuul/ZuulConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2015 the original author or authors. + * Copyright 2013-2017 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. @@ -60,6 +60,7 @@ import com.netflix.zuul.http.ZuulServlet; /** * @author Spencer Gibb * @author Dave Syer + * @author Biju Kunjummen */ @Configuration @EnableConfigurationProperties({ ZuulProperties.class }) diff --git a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/zuul/ZuulRouteApplicationContextInitializer.java b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/zuul/ZuulRouteApplicationContextInitializer.java index 636385a0..5d11d449 100644 --- a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/zuul/ZuulRouteApplicationContextInitializer.java +++ b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/zuul/ZuulRouteApplicationContextInitializer.java @@ -1,3 +1,19 @@ +/* + * Copyright 2017 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.zuul; import org.springframework.cloud.netflix.ribbon.RibbonApplicationContextInitializer; diff --git a/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/ribbon/RibbonApplicationContextInitializerTests.java b/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/ribbon/RibbonApplicationContextInitializerTests.java index e894afb3..9dc129b4 100644 --- a/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/ribbon/RibbonApplicationContextInitializerTests.java +++ b/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/ribbon/RibbonApplicationContextInitializerTests.java @@ -1,3 +1,19 @@ +/* + * Copyright 2017 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 org.junit.Test; diff --git a/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/ribbon/RibbonClientsEagerInitializationTest.java b/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/ribbon/RibbonClientsEagerInitializationTest.java new file mode 100644 index 00000000..288023bf --- /dev/null +++ b/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/ribbon/RibbonClientsEagerInitializationTest.java @@ -0,0 +1,79 @@ +/* + * Copyright 2017 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 org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringRunner; + +import java.util.concurrent.atomic.AtomicInteger; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * @author Biju Kunjummen + */ + +@RunWith(SpringRunner.class) +@SpringBootTest(properties = { + "ribbon.eager-load.enabled=true", + "ribbon.eager-load.serviceIds=testspec1,testspec2" +}) +@DirtiesContext +public class RibbonClientsEagerInitializationTest { + + @Test + public void contextsShouldBeInitialized() { + assertThat(Foo1.getInstanceCount()).isEqualTo(2); + } + + static class FooConfig { + @Bean + public Foo1 foo() { + return new Foo1(); + } + } + + @Configuration + @EnableAutoConfiguration + @RibbonClients( + value = { + @RibbonClient(name="testspec1", configuration = FooConfig.class), + @RibbonClient(name="testspec2", configuration = FooConfig.class), + @RibbonClient(name="testspec3", configuration = FooConfig.class), + }) + static class RibbonConfig { + } + + static class Foo1 { + private static final AtomicInteger INSTANCE_COUNT = new AtomicInteger(); + + public Foo1() { + INSTANCE_COUNT.incrementAndGet(); + } + + public static int getInstanceCount() { + return INSTANCE_COUNT.get(); + } + } +} diff --git a/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/zuul/filters/route/EagerLoadOfZuulConfigurationTests.java b/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/zuul/filters/route/EagerLoadOfZuulConfigurationTests.java index 14dfdd44..ae26538f 100644 --- a/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/zuul/filters/route/EagerLoadOfZuulConfigurationTests.java +++ b/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/zuul/filters/route/EagerLoadOfZuulConfigurationTests.java @@ -1,3 +1,20 @@ +/* + * Copyright 2017 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.zuul.filters.route; import org.junit.Test; diff --git a/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/zuul/filters/route/LazyLoadOfZuulConfigurationTests.java b/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/zuul/filters/route/LazyLoadOfZuulConfigurationTests.java index 280a9191..003f4acf 100644 --- a/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/zuul/filters/route/LazyLoadOfZuulConfigurationTests.java +++ b/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/zuul/filters/route/LazyLoadOfZuulConfigurationTests.java @@ -1,3 +1,20 @@ +/* + * Copyright 2017 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.zuul.filters.route; import com.netflix.loadbalancer.Server; diff --git a/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/zuul/filters/route/apache/HttpClientRibbonCommandIntegrationTests.java b/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/zuul/filters/route/apache/HttpClientRibbonCommandIntegrationTests.java index 14f07e2b..4af144a0 100644 --- a/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/zuul/filters/route/apache/HttpClientRibbonCommandIntegrationTests.java +++ b/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/zuul/filters/route/apache/HttpClientRibbonCommandIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2016 the original author or authors. + * Copyright 2013-2017 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. From 2d9604918a9d064fba10e7849a472473ab38c38e Mon Sep 17 00:00:00 2001 From: Biju Kunjummen Date: Tue, 7 Mar 2017 09:50:44 -0800 Subject: [PATCH 4/5] Changed property name from zuul.ribbon.eager-load to zuul.ribbon.eager-load.enabled --- .../springframework/cloud/netflix/zuul/ZuulConfiguration.java | 2 +- .../zuul/filters/route/EagerLoadOfZuulConfigurationTests.java | 2 +- .../route/apache/HttpClientRibbonCommandIntegrationTests.java | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/zuul/ZuulConfiguration.java b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/zuul/ZuulConfiguration.java index 7bab803c..f3a5791f 100644 --- a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/zuul/ZuulConfiguration.java +++ b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/zuul/ZuulConfiguration.java @@ -165,7 +165,7 @@ public class ZuulConfiguration { } @Bean - @ConditionalOnProperty(value = "zuul.ribbon.eager-load", matchIfMissing = false) + @ConditionalOnProperty(value = "zuul.ribbon.eager-load.enabled", matchIfMissing = false) public ZuulRouteApplicationContextInitializer zuulRoutesApplicationContextInitiazer( SpringClientFactory springClientFactory) { return new ZuulRouteApplicationContextInitializer(springClientFactory, diff --git a/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/zuul/filters/route/EagerLoadOfZuulConfigurationTests.java b/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/zuul/filters/route/EagerLoadOfZuulConfigurationTests.java index ae26538f..2f14ea83 100644 --- a/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/zuul/filters/route/EagerLoadOfZuulConfigurationTests.java +++ b/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/zuul/filters/route/EagerLoadOfZuulConfigurationTests.java @@ -35,7 +35,7 @@ import static org.assertj.core.api.Assertions.assertThat; @RunWith(SpringRunner.class) @SpringBootTest(value = { "zuul.routes.myroute.service-id=eager", - "zuul.ribbon.eager-load=true" }) + "zuul.ribbon.eager-load.enabled=true" }) @DirtiesContext public class EagerLoadOfZuulConfigurationTests { diff --git a/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/zuul/filters/route/apache/HttpClientRibbonCommandIntegrationTests.java b/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/zuul/filters/route/apache/HttpClientRibbonCommandIntegrationTests.java index 4af144a0..6f420150 100644 --- a/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/zuul/filters/route/apache/HttpClientRibbonCommandIntegrationTests.java +++ b/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/zuul/filters/route/apache/HttpClientRibbonCommandIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2017 the original author or authors. + * Copyright 2013-2016 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. @@ -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.context.startup.enabled: true" }) + "zuul.routes.singleton.sensitiveHeaders: " }) @DirtiesContext public class HttpClientRibbonCommandIntegrationTests extends ZuulProxyTestBase { From 427770a6d0b82bb499e94567dbbd664f42493200 Mon Sep 17 00:00:00 2001 From: Biju Kunjummen Date: Tue, 14 Mar 2017 04:35:52 -0500 Subject: [PATCH 5/5] Added documentation, changed property name from serviceIds to clienNames, clients --- .../main/asciidoc/spring-cloud-netflix.adoc | 27 +++++++++++++++++++ .../RibbonApplicationContextInitializer.java | 14 +++++----- .../ribbon/RibbonAutoConfiguration.java | 2 +- .../ribbon/RibbonEagerLoadProperties.java | 10 +++---- .../RibbonClientsEagerInitializationTest.java | 2 +- 5 files changed, 42 insertions(+), 13 deletions(-) diff --git a/docs/src/main/asciidoc/spring-cloud-netflix.adoc b/docs/src/main/asciidoc/spring-cloud-netflix.adoc index df550743..512c8882 100644 --- a/docs/src/main/asciidoc/spring-cloud-netflix.adoc +++ b/docs/src/main/asciidoc/spring-cloud-netflix.adoc @@ -863,6 +863,21 @@ public class MyClass { } ---- +[[ribbon-child-context-eager-load]] +=== Caching of Ribbon Configuration + +Each Ribbon named client has a corresponding child Application Context that Spring Cloud maintains, this application context is lazily loaded up on the first request to the named client. +This lazy loading behavior can be changed to instead eagerly load up these child Application contexts at startup by specifying the names of the Ribbon clients. + +.application.yml +---- +ribbon: + eager-load: + enabled: true + clients: client1, client2, client3 +---- + + [[spring-cloud-feign]] == Declarative REST Client: Feign @@ -2040,6 +2055,18 @@ public class AddResponseHeaderFilter extends ZuulFilter { If an exception is thrown during any portion of the Zuul filter lifecycle, the error filters are executed. The `SendErrorFilter` is only run if `RequestContext.getThrowable()` is not `null`. It then sets specific `javax.servlet.error.*` attributes in the request and forwards the request to the Spring Boot error page. +==== Zuul Eager Application Context Loading + +Zuul internally uses Ribbon for calling the remote url's and Ribbon clients are by default lazily loaded up by Spring Cloud on first call. +This behavior can be changed for Zuul using the following configuration and will result in the child Ribbon related Application contexts being eagerly loaded up at application startup time. + +.application.yml +---- +zuul: + ribbon: + eager-load: + enabled: true +---- == Polyglot support with Sidecar diff --git a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/RibbonApplicationContextInitializer.java b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/RibbonApplicationContextInitializer.java index 3a300aad..1c4881a6 100644 --- a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/RibbonApplicationContextInitializer.java +++ b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/RibbonApplicationContextInitializer.java @@ -31,18 +31,20 @@ public class RibbonApplicationContextInitializer implements ApplicationListener { private final SpringClientFactory springClientFactory; - private final List serviceIds; + + //List of Ribbon client names + private final List clientNames; public RibbonApplicationContextInitializer(SpringClientFactory springClientFactory, - List serviceIds) { + List clientNames) { this.springClientFactory = springClientFactory; - this.serviceIds = serviceIds; + this.clientNames = clientNames; } private void initialize() { - if (serviceIds != null) { - for (String serviceId : serviceIds) { - this.springClientFactory.getContext(serviceId); + if (clientNames != null) { + for (String clientName : clientNames) { + this.springClientFactory.getContext(clientName); } } } diff --git a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/RibbonAutoConfiguration.java b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/RibbonAutoConfiguration.java index 99f67e2b..3eb7737e 100644 --- a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/RibbonAutoConfiguration.java +++ b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/RibbonAutoConfiguration.java @@ -110,7 +110,7 @@ public class RibbonAutoConfiguration { @ConditionalOnProperty(value = "ribbon.eager-load.enabled", matchIfMissing = false) public RibbonApplicationContextInitializer ribbonApplicationContextInitializer() { return new RibbonApplicationContextInitializer(springClientFactory(), - ribbonEagerLoadProperties.getServiceIds()); + ribbonEagerLoadProperties.getClients()); } @Configuration diff --git a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/RibbonEagerLoadProperties.java b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/RibbonEagerLoadProperties.java index 7b350929..4589edb3 100644 --- a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/RibbonEagerLoadProperties.java +++ b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/RibbonEagerLoadProperties.java @@ -29,7 +29,7 @@ import java.util.List; @ConfigurationProperties(prefix = "ribbon.eager-load") public class RibbonEagerLoadProperties { private boolean enabled = false; - private List serviceIds; + private List clients; public boolean isEnabled() { return enabled; @@ -39,11 +39,11 @@ public class RibbonEagerLoadProperties { this.enabled = enabled; } - public List getServiceIds() { - return serviceIds; + public List getClients() { + return clients; } - public void setServiceIds(List serviceIds) { - this.serviceIds = serviceIds; + public void setClients(List clients) { + this.clients = clients; } } diff --git a/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/ribbon/RibbonClientsEagerInitializationTest.java b/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/ribbon/RibbonClientsEagerInitializationTest.java index 288023bf..55fc9676 100644 --- a/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/ribbon/RibbonClientsEagerInitializationTest.java +++ b/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/ribbon/RibbonClientsEagerInitializationTest.java @@ -37,7 +37,7 @@ import static org.assertj.core.api.Assertions.assertThat; @RunWith(SpringRunner.class) @SpringBootTest(properties = { "ribbon.eager-load.enabled=true", - "ribbon.eager-load.serviceIds=testspec1,testspec2" + "ribbon.eager-load.clients=testspec1,testspec2" }) @DirtiesContext public class RibbonClientsEagerInitializationTest {