spring-cloud-zookeeper icon indicating copy to clipboard operation
spring-cloud-zookeeper copied to clipboard

Wait until ApplicationReadyEvent to register

Open ktong opened this issue 9 years ago • 14 comments

Use Zuul with ZookeeperDiscovery, and Zuul return 404 when deploying services. The reason seems that ZookeeperDiscovery register service in start of LifeCycle. In that time, the container has no beed fully started so that any request returns 404.

Is there any work around to delay registration of ZookeeperDiscovery?

Thanks

ktong avatar Sep 20 '16 21:09 ktong

There will be a delay between when services are registered in zookeeper and when they are available in zuul. Unsure how delaying registration of ZookeeperDiscovery would help.

spencergibb avatar Sep 20 '16 21:09 spencergibb

Is there any way to delay registration until health check return up? Like what Eureka did.

ktong avatar Sep 20 '16 22:09 ktong

I'm still not sure what you are asking for. What did Eureka do?

spencergibb avatar Sep 21 '16 00:09 spencergibb

The thing is that, after it was registered on zookeeper, the service may take some time to finish its initialization to serve incoming requests. During this period, other services may discover it and send request to it and then get a 404 error.

It would be great if the registration could be the last step of starting up, which is, after the service is ready.

PS: This 404 error was not observed when using Eureka.

jameslan avatar Sep 21 '16 17:09 jameslan

Eureka doesn't do anything specific to avoid this, it just takes up to 90 seconds to show up because of the various cache/wait times.

spencergibb avatar Sep 22 '16 14:09 spencergibb

it shows up early than expectation. Zuul pick the service before it's fully started. So requests sent to this service get 404 error.

ktong avatar Sep 22 '16 14:09 ktong

No need to repeat the problem. How would you know the service is "fully started"?

spencergibb avatar Sep 22 '16 15:09 spencergibb

May we use ApplicationStartedEvent in Spring boot to detect "fully started"?

ktong avatar Sep 22 '16 17:09 ktong

Registration already happens with the spring boot lifecycle. ApplicationStartedEvent seems too early to me according to the docs. ApplicationReadyEvent seems like a better candidate.

spencergibb avatar Sep 22 '16 19:09 spencergibb

agree. ApplicationReadyEvent is better.

ktong avatar Sep 22 '16 19:09 ktong

A temporary patch that verifies ApplicationReadyEvent is good moment to register service.

@Configuration
@ConditionalOnProperty(value = "spring.cloud.zookeeper.discovery.enabled", matchIfMissing = true)
@AutoConfigureBefore(ZookeeperDiscoveryClientConfiguration.class)
public class ZookeeperConfig {
  @Autowired
  private CuratorFramework curator;

  @Bean
  public ZookeeperServiceDiscovery zookeeperServiceDiscovery(ZookeeperDiscoveryProperties zookeeperDiscoveryProperties,
      InstanceSerializer<ZookeeperInstance> instanceSerializer) {
    return new DeferZookeeperServiceDiscovery(this.curator, zookeeperDiscoveryProperties, instanceSerializer);
  }

  public static class DeferZookeeperServiceDiscovery extends ZookeeperServiceDiscovery {
    public DeferZookeeperServiceDiscovery(CuratorFramework curator, ZookeeperDiscoveryProperties properties,
        InstanceSerializer<ZookeeperInstance> instanceSerializer) {
      super(curator, properties, instanceSerializer);
    }

    @Override
    public ServiceDiscovery<ZookeeperInstance> getServiceDiscovery() {
      return new NoOpServiceDiscovery<>();
    }

    @EventListener
    @SneakyThrows
    public void handleContextRefresh(ApplicationReadyEvent event) {
      super.getServiceDiscovery().start();
    }

    private static class NoOpServiceDiscovery<T> implements ServiceDiscovery<T> {
      @Override
      public void start()
          throws Exception {

      }

      @Override
      public void registerService(ServiceInstance<T> service)
          throws Exception {

      }

      @Override
      public void updateService(ServiceInstance<T> service)
          throws Exception {

      }

      @Override
      public void unregisterService(ServiceInstance<T> service)
          throws Exception {

      }

      @Override
      public ServiceCacheBuilder<T> serviceCacheBuilder() {
        return null;
      }

      @Override
      public Collection<String> queryForNames()
          throws Exception {
        return null;
      }

      @Override
      public Collection<ServiceInstance<T>> queryForInstances(String name)
          throws Exception {
        return null;
      }

      @Override
      public ServiceInstance<T> queryForInstance(String name, String id)
          throws Exception {
        return null;
      }

      @Override
      public ServiceProviderBuilder<T> serviceProviderBuilder() {
        return null;
      }

      @Override
      public void close()
          throws IOException {

      }
    }
  }
}

ktong avatar Sep 28 '16 17:09 ktong

It seems deregister services a little bit later so it also causes 404 error in a very short duration. But I could not find better ApplicationEvent to know when Spring is starting to shutdown.

ktong avatar Sep 28 '16 18:09 ktong

This might be difficult because of the multiple application contexts (ie bootstrap) that are part of a spring cloud application.

spencergibb avatar Oct 03 '16 20:10 spencergibb

+1 (delay discovery registration until ApplicationReadyEvent is fired)

ochrist-eis avatar Jul 27 '17 17:07 ochrist-eis