spring-cloud-openfeign: Feign client lazy initialization fails when using CompletableFuture.supplyAsync() to call the Feign client initially
Describe the bug
Feign client initialization is always lazy. As such, the first calling thread triggers the initialization, which may be another Spring component (e.g. REST controller) using CompletableFuture.supplyAsync()
to do so. This uses ForkJoinWorkerThread
underneath, resulting in a ClassNotFoundException
, if and only if running in a dockerized environment based on Spring Boot Build Image plugin.
Using other JVM Executors(except newWorkStealingExecutor)
, e.g. fixedSizeThreadExecutor()
, does not result in this behavior. Also eagerly initializing the Feign client by calling it synchronously, e.g. from within a CommandLineRunner
, circumvents this. Once, the Feign client is properly initialized, there is no problem, however, Feign does not yet support eager initialization.
Sample A sample project with instructions to reproduce is provided here: https://github.com/maverick1601/openfeign-build-image-error
About this issue
- Original URL
- State: closed
- Created 3 years ago
- Reactions: 3
- Comments: 29
Links to this issue
Commits related to this issue
- Inject the class loader from the parent context into the child context more aggressively. `ConditionEvaluator` extracts the class loader from the bean factory at instantiation, and then uses said cla... — committed to tommyk-gears/spring-cloud-commons by tommyk-gears 2 years ago
- Inject the class loader from the parent context into the child context more aggressively. `ConditionEvaluator` extracts the class loader from the bean factory at instantiation, and then uses said cla... — committed to tommyk-gears/spring-cloud-commons by tommyk-gears 2 years ago
Spring boot 3.1.0 spring-cloud 2022.0.3 Java 17
The previous workaround is working with little changes
The previous workaround didn’t work for me as the constructor of LoadBalancerClientFactory without arguments is deprecated, as a result that solution causes NPE.
Here is the modified version:
We got similar issue in one of our applications. After investigation, we found this is caused by https://bugs.openjdk.org/browse/JDK-8172726.
Start with Java 9,
ForkJoinPool
creates thread using system class loader as context class loader, refer to https://github.com/openjdk/jdk11u/blob/master/src/java.base/share/classes/java/util/concurrent/ForkJoinPool.java#L721.If we run applications in IDE, the system class loader has been used to load Spring and application classes, so it’s not easy to reproduce this issue. If we run applications from command line using fat JAR,
org.springframework.boot.loader.LaunchedURLClassLoader
will be used to load those classes instead. If threads switch to use system class loader, it will not be able to load Spring and other classes inside the fat JAR.An alternative solution is to use rxjava,
mvn dep
<dependency> <groupId>io.reactivex</groupId> <artifactId>rxjava</artifactId> </dependency>This issue is not within the team’s priorities right now, but is open to community contributions.
@maverick1601, thanks for reporting this. Will discuss it with the team and get back to you.
Our applications use a different workaround. We created
ApplicationContextInitializer
implementation (and added tospring.factories
) to manually set the class loader as the context class loader who starts the application.The fix in https://github.com/spring-cloud/spring-cloud-commons/pull/1098 tries to use the class loader of parent
ApplicationContext
in childApplicationContext
:However typically the parent
ApplicationContext
doesn’t set class loader explicitly, it will useClassUtils.getDefaultClassLoader()
to return the default class loader. Unfortunately that implementation will return context class loader if that has been set.I’m facing the exact same problem after an update from Spring 2.3 to Spring 2.7 + Java update from 14 to 17. We are not using any @Async or CompletableFuture, but the feign client is called by a Grizzly Thread (started as part of a Spring Boot app) that is part of our core infrastructure and needs to call another running Spring Boot app via Feign. This used to work without any problems before the update. Disappointed way too often to see such an issue open for such a long time with no prospect of a solution
Hi, is there any news about this issue and the associated PR? I encountered a similar issue when using openfeign client called within a method annotated as
@Async
that uses a ForkJoinPool executor (spring-boot: 2.5.5, spring-cloud: 2020.0.4). I have yet to find a solution or workaround.P.S. I think it’s related to #600
P.S.S. I’ve tried the solution described in #600 and I managed to make it working. I hope that it can be fixed asap because it’s not only a openfeign issue but it does apply on LoadBalanced RestTemplates too
We experience the same issue with k8s resource limits. After we removed the CPU limit of 1, our application failed to execute Feign requests as the POD where the application runs now had more CPUs visible to the JVM. 4 instead of 1. I’m wondering why the Spring team does not see this as a serious issue as the usage of
CompletableFuture
in a project is very likely and we have numerous comments here with lots of related issues that were closed …If you have a custom loadbalancer configure, like
@LoadBalancerClients(defaultConfiguration = CustomLoadBalancerConfiguration.class)
you should add itloadBalancerClientFactory.setConfigurations(configurations.getIfAvailable(Collections::emptyList));
I wanted to share some information about some similar bug.
We have one spring service working with docker, k8, java 17 and the use of CompletableFuture, everything was working ok, until we decided to add more memory and cpu
We pass from
To
And the same bug appear
Could not find class [org.springframework.boot.autoconfigure.condition.OnBeanCondition]
the classLoader was almost empty, like we pass from 15000 class to 50 in the classLoader.But the the code was related to StreamBridge and not feign client (even if we use it in the code, but not directly in the code called), but for me is the same JVM/spring bug. Is was working ok with the IDE, but the Jar was failing, etc
The solution put in place was add
And pass it when we call the CompletableFuture
Made another sample project that reproduces a problem. It also contains a submodule with proposed workaround by @DenisKorolev. There are also a test with boot 2.3 and ribbon that works fine and a test for when eager-loading enabled for Spring’s loadbalancer (available in boot 3+, but you can implement the same logic for yourself if it fits).
My problem like it, #1139
But my problem is deadlock, Maybe it’s the same thing with different consequences
@DenisKorolev is there any particular reason why you have omitted the following line from the previous workaround?
This line still appears in the
org.springframework.cloud.loadbalancer.config.LoadBalancerAutoConfiguration
.