diff --git a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/feign/ribbon/CachingLBClientFactory.java b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/feign/ribbon/CachingLBClientFactory.java new file mode 100644 index 00000000..604434ad --- /dev/null +++ b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/feign/ribbon/CachingLBClientFactory.java @@ -0,0 +1,47 @@ +/* + * 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.feign.ribbon; + +import feign.ribbon.LBClient; +import feign.ribbon.LBClientFactory; +import org.springframework.util.ConcurrentReferenceHashMap; + +import java.util.Map; + +/** + * LBClientFactory that caches entries created. + * @author Spencer Gibb + */ +public class CachingLBClientFactory implements LBClientFactory { + + private volatile Map cache = new ConcurrentReferenceHashMap<>(); + private final LBClientFactory delegate; + + public CachingLBClientFactory(LBClientFactory delegate) { + this.delegate = delegate; + } + + @Override + public LBClient create(String clientName) { + if (cache.containsKey(clientName)) { + return cache.get(clientName); + } + LBClient client = delegate.create(clientName); + cache.put(clientName, client); + return client; + } +} diff --git a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/feign/ribbon/FeignRibbonClientAutoConfiguration.java b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/feign/ribbon/FeignRibbonClientAutoConfiguration.java index e70623ee..2889cf56 100644 --- a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/feign/ribbon/FeignRibbonClientAutoConfiguration.java +++ b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/feign/ribbon/FeignRibbonClientAutoConfiguration.java @@ -16,7 +16,6 @@ package org.springframework.cloud.netflix.feign.ribbon; -import feign.ribbon.LBClientFactory; import feign.ribbon.RibbonClient; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.AutoConfigureBefore; @@ -47,13 +46,18 @@ public class FeignRibbonClientAutoConfiguration { private SpringClientFactory factory; @Bean - public LBClientFactory lbClientFactory() { + public SpringLBClientFactory springLBClientFactory() { return new SpringLBClientFactory(factory); } + @Bean + public CachingLBClientFactory cachingLBClientFactory() { + return new CachingLBClientFactory(springLBClientFactory()); + } + @Bean @ConditionalOnMissingBean public Client feignRibbonClient() { - return RibbonClient.builder().lbClientFactory(lbClientFactory()).build(); + return RibbonClient.builder().lbClientFactory(cachingLBClientFactory()).build(); } } \ No newline at end of file diff --git a/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/feign/ribbon/CachingLBClientFactoryTests.java b/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/feign/ribbon/CachingLBClientFactoryTests.java new file mode 100644 index 00000000..7a2e7045 --- /dev/null +++ b/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/feign/ribbon/CachingLBClientFactoryTests.java @@ -0,0 +1,78 @@ +/* + * 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.feign.ribbon; + +import static org.junit.Assert.*; +import static org.mockito.Mockito.*; + +import com.netflix.client.config.CommonClientConfigKey; +import com.netflix.client.config.DefaultClientConfigImpl; +import com.netflix.client.config.IClientConfig; +import feign.ribbon.LBClient; +import feign.ribbon.LBClientFactory; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +/** + * @author Spencer Gibb + */ +public class CachingLBClientFactoryTests { + + @Mock + private LBClientFactory delegate; + + private CachingLBClientFactory factory; + + @Before + public void init() { + MockitoAnnotations.initMocks(this); + + IClientConfig config = new DefaultClientConfigImpl(); + config.set(CommonClientConfigKey.ConnectTimeout, 1000); + config.set(CommonClientConfigKey.ReadTimeout, 500); + + LBClient client1 = LBClient.create(null, config); + LBClient client2 = LBClient.create(null, config); + + when(delegate.create("client1")).thenReturn(client1); + when(delegate.create("client2")).thenReturn(client2); + + factory = new CachingLBClientFactory(delegate); + } + + @Test + public void delegateCreatesWhenMissing() { + LBClient client = factory.create("client1"); + assertNotNull("client was null", client); + + verify(delegate, times(1)).create("client1"); + } + + @Test + public void cacheWorks() { + LBClient client = factory.create("client2"); + assertNotNull("client was null", client); + + client = factory.create("client2"); + assertNotNull("client was null", client); + + verify(delegate, times(1)).create("client2"); + } + +}