Skip to content

Commit a5303ee

Browse files
committed
Add defensive warning for multiple Feign Client beans to prevent potential resource exhaustion
Signed-off-by: 98001yash <yashchauhan.gaya@gmail.com>
1 parent 4122b62 commit a5303ee

1 file changed

Lines changed: 58 additions & 4 deletions

File tree

spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/FeignClientFactoryBean.java

Lines changed: 58 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,38 @@
5959
import org.springframework.util.StringUtils;
6060

6161
/**
62+
* A {@link FactoryBean} responsible for creating Feign client proxies.
63+
*
64+
* <p><strong>Note:</strong> Custom {@link Client} beans provided in a Feign client
65+
* context should be singleton-scoped. Defining a prototype-scoped {@code Client}
66+
* may lead to multiple underlying HTTP connection pools being created, which
67+
* can result in resource exhaustion.
68+
*
69+
* @author Spencer Gibb
70+
* @author Venil Noronha
71+
* @author Eko Kurniawan Khannedy
72+
* @author Gregor Zurowski
73+
* @author Matt King
74+
* @author Olga Maciaszek-Sharma
75+
* @author Ilia Ilinykh
76+
* @author Marcin Grzejszczak
77+
* @author Jonatan Ivanov
78+
* @author Sam Kruglov
79+
* @author Jasbir Singh
80+
* @author Hyeonmin Park
81+
* @author Felix Dittrich
82+
* @author Dominique Villard
83+
* @author Can Bezmen
84+
*/
85+
/**
86+
* A {@link FactoryBean} responsible for creating Feign client proxies.
87+
*
88+
* <p>
89+
* <strong>Note:</strong> Custom {@link Client} beans provided in a Feign client context
90+
* should be singleton-scoped. Defining a prototype-scoped {@code Client} may lead to
91+
* multiple underlying HTTP connection pools being created, which can result in resource
92+
* exhaustion.
93+
*
6294
* @author Spencer Gibb
6395
* @author Venil Noronha
6496
* @author Eko Kurniawan Khannedy
@@ -465,42 +497,64 @@ public Object getObject() {
465497
<T> T getTarget() {
466498
FeignClientFactory feignClientFactory = beanFactory != null ? beanFactory.getBean(FeignClientFactory.class)
467499
: applicationContext.getBean(FeignClientFactory.class);
500+
468501
Feign.Builder builder = feign(feignClientFactory);
502+
469503
if (!StringUtils.hasText(url) && !isUrlAvailableInConfig(contextId)) {
470504

471505
if (LOG.isInfoEnabled()) {
472506
LOG.info("For '" + name + "' URL not provided. Will try picking an instance via load-balancing.");
473507
}
508+
474509
if (!name.startsWith("http://") && !name.startsWith("https://")) {
475510
url = "http://" + name;
476511
}
477512
else {
478513
url = name;
479514
}
515+
480516
url += cleanPath();
481517
return (T) loadBalance(builder, feignClientFactory, new HardCodedTarget<>(type, name, url));
482518
}
519+
483520
if (StringUtils.hasText(url) && !url.startsWith("http://") && !url.startsWith("https://")) {
484521
url = "http://" + url;
485522
}
523+
486524
Client client = getOptional(feignClientFactory, Client.class);
525+
487526
if (client != null) {
527+
528+
try {
529+
Map<String, Client> clients = feignClientFactory.getInstances(contextId, Client.class);
530+
531+
if (clients != null && clients.size() > 1) {
532+
LOG.warn("Multiple Feign Client beans detected for contextId '" + contextId
533+
+ "'. Custom Client beans should typically be singleton-scoped "
534+
+ "to avoid multiple underlying connection pools.");
535+
}
536+
}
537+
catch (Exception ex) {
538+
if (LOG.isDebugEnabled()) {
539+
LOG.debug("Unable to inspect Feign Client beans for contextId '" + contextId + "'", ex);
540+
}
541+
}
542+
488543
if (client instanceof FeignBlockingLoadBalancerClient) {
489-
// not load balancing because we have a url,
490-
// but Spring Cloud LoadBalancer is on the classpath, so unwrap
491544
client = ((FeignBlockingLoadBalancerClient) client).getDelegate();
492545
}
546+
493547
if (client instanceof RetryableFeignBlockingLoadBalancerClient) {
494-
// not load balancing because we have a url,
495-
// but Spring Cloud LoadBalancer is on the classpath, so unwrap
496548
client = ((RetryableFeignBlockingLoadBalancerClient) client).getDelegate();
497549
}
550+
498551
builder.client(client);
499552
}
500553

501554
applyBuildCustomizers(feignClientFactory, builder);
502555

503556
Targeter targeter = get(feignClientFactory, Targeter.class);
557+
504558
return targeter.target(this, builder, feignClientFactory, resolveTarget(feignClientFactory, contextId, url));
505559
}
506560

0 commit comments

Comments
 (0)