序
本文主要研究一下eureka client的shutdown
EurekaRegistration
spring-cloud-netflix-eureka-client-2.0.0.RC1-sources.jar!/org/springframework/cloud/netflix/eureka/EurekaClientAutoConfiguration.java
@Bean @ConditionalOnBean(AutoServiceRegistrationProperties.class) @ConditionalOnProperty(value = "spring.cloud.service-registry.auto-registration.enabled", matchIfMissing = true) public EurekaRegistration eurekaRegistration(EurekaClient eurekaClient, CloudEurekaInstanceConfig instanceConfig, ApplicationInfoManager applicationInfoManager, ObjectProviderhealthCheckHandler) { return EurekaRegistration.builder(instanceConfig) .with(applicationInfoManager) .with(eurekaClient) .with(healthCheckHandler) .build(); } @Bean @ConditionalOnBean(AutoServiceRegistrationProperties.class) @ConditionalOnProperty(value = "spring.cloud.service-registry.auto-registration.enabled", matchIfMissing = true) public EurekaAutoServiceRegistration eurekaAutoServiceRegistration(ApplicationContext context, EurekaServiceRegistry registry, EurekaRegistration registration) { return new EurekaAutoServiceRegistration(context, registry, registration); } //... @Bean(destroyMethod = "shutdown") @ConditionalOnMissingBean(value = EurekaClient.class, search = SearchStrategy.CURRENT) public EurekaClient eurekaClient(ApplicationInfoManager manager, EurekaClientConfig config) { return new CloudEurekaClient(manager, config, this.optionalArgs, this.context); }
这里自动创建EurekaRegistration,以及EurekaAutoServiceRegistration,EurekaClient
EurekaClient
eureka-client-1.8.8-sources.jar!/com/netflix/discovery/EurekaClient.java
可以看到EurekaClient标注了@Bean(destroyMethod = "shutdown"),也就是在bean销毁的时候,会触发eurekaClient.shutdown方法
EurekaRegistration
spring-cloud-netflix-eureka-client-2.0.0.RC1-sources.jar!/org/springframework/cloud/netflix/eureka/serviceregistry/EurekaRegistration.java
public class EurekaRegistration implements Registration, Closeable { //...... @Override public void close() throws IOException { this.eurekaClient.shutdown(); }}
这里实现了Closeable接口的close方法,在spring容器关闭的时候触发,这里调用了eurekaClient.shutdown()
EurekaAutoServiceRegistration
spring-cloud-netflix-eureka-client-2.0.0.RC1-sources.jar!/org/springframework/cloud/netflix/eureka/serviceregistry/EurekaAutoServiceRegistration.java
@Override public void start() { // only set the port if the nonSecurePort or securePort is 0 and this.port != 0 if (this.port.get() != 0) { if (this.registration.getNonSecurePort() == 0) { this.registration.setNonSecurePort(this.port.get()); } if (this.registration.getSecurePort() == 0 && this.registration.isSecure()) { this.registration.setSecurePort(this.port.get()); } } // only initialize if nonSecurePort is greater than 0 and it isn't already running // because of containerPortInitializer below if (!this.running.get() && this.registration.getNonSecurePort() > 0) { this.serviceRegistry.register(this.registration); this.context.publishEvent( new InstanceRegisteredEvent<>(this, this.registration.getInstanceConfig())); this.running.set(true); } } @Override public void stop() { this.serviceRegistry.deregister(this.registration); this.running.set(false); }
这里的start,stop实现的是Lifecycle的方法,也就在spring容器关闭的时候,会触发stop方法,然后调用的是serviceRegistry.deregister(this.registration)
RefreshScopeRefreshedEvent
spring-cloud-netflix-eureka-client-2.0.0.RC1-sources.jar!/org/springframework/cloud/netflix/eureka/EurekaDiscoveryClientConfiguration.java
@Configuration @ConditionalOnClass(RefreshScopeRefreshedEvent.class) protected static class EurekaClientConfigurationRefresher { @Autowired(required = false) private EurekaClient eurekaClient; @Autowired(required = false) private EurekaAutoServiceRegistration autoRegistration; @EventListener(RefreshScopeRefreshedEvent.class) public void onApplicationEvent(RefreshScopeRefreshedEvent event) { //This will force the creation of the EurkaClient bean if not already created //to make sure the client will be reregistered after a refresh event if(eurekaClient != null) { eurekaClient.getApplications(); } if (autoRegistration != null) { // register in case meta data changed this.autoRegistration.stop(); this.autoRegistration.start(); } } }
接收到RefreshScopeRefreshedEvent的时候,会先stop,再start
DiscoveryClient.shutdown
eureka-client-1.8.8-sources.jar!/com/netflix/discovery/DiscoveryClient.java
public synchronized void shutdown() { if (isShutdown.compareAndSet(false, true)) { logger.info("Shutting down DiscoveryClient ..."); if (statusChangeListener != null && applicationInfoManager != null) { applicationInfoManager.unregisterStatusChangeListener(statusChangeListener.getId()); } cancelScheduledTasks(); // If APPINFO was registered if (applicationInfoManager != null && clientConfig.shouldRegisterWithEureka() && clientConfig.shouldUnregisterOnShutdown()) { applicationInfoManager.setInstanceStatus(InstanceStatus.DOWN); unregister(); } if (eurekaTransport != null) { eurekaTransport.shutdown(); } heartbeatStalenessMonitor.shutdown(); registryStalenessMonitor.shutdown(); logger.info("Completed shut down of DiscoveryClient"); } } /** * unregister w/ the eureka service. */ void unregister() { // It can be null if shouldRegisterWithEureka == false if(eurekaTransport != null && eurekaTransport.registrationClient != null) { try { logger.info("Unregistering ..."); EurekaHttpResponsehttpResponse = eurekaTransport.registrationClient.cancel(instanceInfo.getAppName(), instanceInfo.getId()); logger.info(PREFIX + "{} - deregister status: {}", appPathIdentifier, httpResponse.getStatusCode()); } catch (Exception e) { logger.error(PREFIX + "{} - de-registration failed{}", appPathIdentifier, e.getMessage(), e); } } }
这里可以看到,先设置状态为DOWN,然后调用cancel方法
RestTemplateEurekaHttpClient
spring-cloud-netflix-eureka-client-2.0.0.RC1-sources.jar!/org/springframework/cloud/netflix/eureka/http/RestTemplateEurekaHttpClient.java
public EurekaHttpResponsecancel(String appName, String id) { String urlPath = serviceUrl + "apps/" + appName + '/' + id; ResponseEntity response = restTemplate.exchange(urlPath, HttpMethod.DELETE, null, Void.class); return anEurekaHttpResponse(response.getStatusCodeValue()) .headers(headersOf(response)).build(); } @Override public EurekaHttpResponse register(InstanceInfo info) { String urlPath = serviceUrl + "apps/" + info.getAppName(); HttpHeaders headers = new HttpHeaders(); headers.add(HttpHeaders.ACCEPT_ENCODING, "gzip"); headers.add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE); ResponseEntity response = restTemplate.exchange(urlPath, HttpMethod.POST, new HttpEntity<>(info, headers), Void.class); return anEurekaHttpResponse(response.getStatusCodeValue()) .headers(headersOf(response)).build(); }
cancel方法就是调用REST的DELETE操作,注销掉服务
小结
springcloud封装的eureka自动注册,关闭的时候主要分两大类:
- 依赖生命周期在销毁时调用shutdown、close
EurekaRegistration是在close的时候,会触发eurekaClient.shutdown方法 EurekaAutoServiceRegistration则在stop的时候,标记状态为DOWN,发布StatusChangeEvent事件 EurekaClient则标注了@Bean(destroyMethod = "shutdown"),也就是在bean销毁的时候,会触发eurekaClient.shutdown方法
- 期间状态变更发布StatusChangeEvent事件
com/netflix/discovery/DiscoveryClient.java有个StatusChangeListener,状态变更在需要的时候,会触发InstanceInfoReplicator的onDemandUpdate方法,该方法会调用discoveryClient.register()去与eureka server更新自身实例的状态。 这里相当于变相通过register接口更改status状态。
这里要区分一下cancel与StatusChangeEvent的区别,cancel是从eureka server删掉这条instanceInfo,而StatusChangeEvent变更状态,不会删除这条instanceInfo,只是更新status状态(status状态一共有UP,DOWN,STARTING,OUT_OF_SERVICE,UNKNOWN几类)。