Spring-cloud-netflix: Eureka: eurekaClient shutdown on context /pause but won't restart on /resume

Created on 6 Jul 2017  路  14Comments  路  Source: spring-cloud/spring-cloud-netflix

EurekaServiceRegistry.unregister() shuts down the EurekaClient when asked to unregister the current registration.
The EurekaAutoServiceRegistration is a SmartLifecycle whose stop() method is invoked when the current context is closed. When called, it tries to unregister the application.

--> there is no need to shutdown the Eureka client at this point - unregistering the application is enough.

Problem is nothing will restart the Eureka client when the context is (re-)started, during a /resume for instance.

The CloudEurekaClient is registered as a @Bean(destroyMethod = "shutdown") in the application context. So its shutdown method will be automatically invoked when the application context is destroyed.

--> we could remove the call to eurekaClient.shutdown() from EurekaServiceRegistry and let Spring invoke the destroy method when it feels appropriate. However, this approach is causing the following exception:

Invocation of destroy method failed on bean with name 'scopedTarget.eurekaClient': org.springframework.beans.factory.BeanCreationNotAllowedException: Error creating bean with name 'eurekaInstanceConfigBean': Singleton bean creation not allowed while singletons of this factory are in destruction (Do not request a bean from a BeanFactory in a destroy method implementation!)

bug

Most helpful comment

the fix will be in the Edgware release

All 14 comments

What version of Spring Cloud are you using? It sounds similar to #1857.

It can be reproduced with any version up to Dalston.SR1 included.
I created a simple application using the Spring Initializr by including the features Web, Eureka and Actuator. Here is a short excerpt of the logs:

Once the application is started, try to _pause_ it via a call to POST /pause:

(1) 2017-07-10 22:53:26.286  INFO 3844 --- [nio-8080-exec-1] o.s.c.support.DefaultLifecycleProcessor  : Stopping beans in phase 0
(2) 2017-07-10 22:53:26.287  INFO 3844 --- [nio-8080-exec-1] o.s.c.n.e.s.EurekaServiceRegistry        : Unregistering application unknown with eureka with status DOWN
(3) 2017-07-10 22:53:26.287  WARN 3844 --- [nio-8080-exec-1] com.netflix.discovery.DiscoveryClient    : Saw local status change event StatusChangeEvent [timestamp=1499720006287, current=DOWN, previous=UP]
(4) 2017-07-10 22:53:26.287  INFO 3844 --- [nfoReplicator-0] com.netflix.discovery.DiscoveryClient    : DiscoveryClient_UNKNOWN/macfury: registering service...
(5) 2017-07-10 22:53:26.287  INFO 3844 --- [nio-8080-exec-1] com.netflix.discovery.DiscoveryClient    : Shutting down DiscoveryClient ...
(6) 2017-07-10 22:53:26.288  INFO 3844 --- [nio-8080-exec-1] com.netflix.discovery.DiscoveryClient    : Unregistering ...
(7) 2017-07-10 22:53:26.304  INFO 3844 --- [nfoReplicator-0] com.netflix.discovery.DiscoveryClient    : DiscoveryClient_UNKNOWN/macfury - registration status: 204
(8) 2017-07-10 22:53:26.317  INFO 3844 --- [nio-8080-exec-1] com.netflix.discovery.DiscoveryClient    : DiscoveryClient_UNKNOWN/macfury - deregister  status: 200
(9) 2017-07-10 22:53:26.321  INFO 3844 --- [nio-8080-exec-1] com.netflix.discovery.DiscoveryClient    : Completed shut down of DiscoveryClient

The application's status is changed to DOWN and the new status is propagated to the Eureka server (see thread InstanceInfoReplicator-0 on lines 4 and 7).
The Eureka Client is shutdown at line 5 and is completely dead at line 9.

Notice that the replication of the instance status and the shutdown of the client happen on TWO different threads. In this example we are lucky the unregister action (line 8) happened after the status update (line 7)...

Now attempt to resume the application with a call to POST /resume:

2017-07-10 22:53:43.935  INFO 3844 --- [nio-8080-exec-2] o.s.c.support.DefaultLifecycleProcessor  : Starting beans in phase 0
2017-07-10 22:53:43.935  INFO 3844 --- [nio-8080-exec-2] o.s.c.n.e.s.EurekaServiceRegistry        : Registering application unknown with eureka with status UP

The EurekaServiceRegistry simply update the InstanceStatus - but nothing is listening to these changes anymore since he EurekaClient is dead - and nothing will restart it.
Nothing will happen anymore beyond this point: the application won't be registered with the Eureka server anymore nor any heartbeat will be sent.

What looks like happens is the DiscoveryClient bean (in this case CloudDiscoveryClient) is left in a "shutdown" (isShutdown is true) state when the pause even occurs. When the resume even occurs we go through the right steps to reregister the client but since the DiscoveryClient thinks it is shutdown nothing happens. I don't see a public way of changing the shutdown state at the moment but I will look into it.

According to me, it deregister should not shutdown the client at this point (see my original comments). The client should be closed only when the application is shutdown by the same component that used to start it. The lifecycle of the client would be cleaner this way.

I have the same issue. Any workaround in sight ?

the fix will be in the Edgware release

I am getting the same issue in Edgeware release as well.
"Error creating bean with name 'eurekaAutoServiceRegistration'"

@dulimitta if you believe there is still an issue, please open a new issue with a project and steps to reproduce the problem

I use Edgware.SR3, but still have this exception when shutdown project.
use @FeignClient it must have exception.
@ryanjbaxter Is there a way to fix it?

@dchack
I've added below code to fix this issue:

@Component
public class FeignBeanFactoryPostProcessor implements BeanFactoryPostProcessor {

@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
    if (containsBeanDefinition(beanFactory, "feignContext", "eurekaAutoServiceRegistration")) {
        BeanDefinition bd = beanFactory.getBeanDefinition("feignContext");
        bd.setDependsOn("eurekaAutoServiceRegistration");
    }
}

private boolean containsBeanDefinition(ConfigurableListableBeanFactory beanFactory, String... beans) {
    return Arrays.stream(beans).allMatch(b -> beanFactory.containsBeanDefinition(b));
}

}

The code depend on this url: https://github.com/dengly/spring-cloud-study/issues/5

Please stop spamming multiple issues

Was this page helpful?
0 / 5 - 0 ratings