本文主要研究一下eureka client的shutdownhtml
spring-cloud-netflix-eureka-client-2.0.0.RC1-sources.jar!/org/springframework/cloud/netflix/eureka/EurekaClientAutoConfiguration.javajava
@Bean @ConditionalOnBean(AutoServiceRegistrationProperties.class) @ConditionalOnProperty(value = "spring.cloud.service-registry.auto-registration.enabled", matchIfMissing = true) public EurekaRegistration eurekaRegistration(EurekaClient eurekaClient, CloudEurekaInstanceConfig instanceConfig, ApplicationInfoManager applicationInfoManager, ObjectProvider<HealthCheckHandler> healthCheckHandler) { 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,EurekaClientspring
eureka-client-1.8.8-sources.jar!/com/netflix/discovery/EurekaClient.javaapp
能夠看到EurekaClient標註了@Bean(destroyMethod = "shutdown"),也就是在bean銷燬的時候,會觸發eurekaClient.shutdown方法ide
spring-cloud-netflix-eureka-client-2.0.0.RC1-sources.jar!/org/springframework/cloud/netflix/eureka/serviceregistry/EurekaRegistration.javaui
public class EurekaRegistration implements Registration, Closeable { //...... @Override public void close() throws IOException { this.eurekaClient.shutdown(); } }
這裏實現了Closeable接口的close方法,在spring容器關閉的時候觸發,這裏調用了eurekaClient.shutdown()this
spring-cloud-netflix-eureka-client-2.0.0.RC1-sources.jar!/org/springframework/cloud/netflix/eureka/serviceregistry/EurekaAutoServiceRegistration.javaurl
@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)rest
spring-cloud-netflix-eureka-client-2.0.0.RC1-sources.jar!/org/springframework/cloud/netflix/eureka/EurekaDiscoveryClientConfiguration.javacode
@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
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 ..."); EurekaHttpResponse<Void> httpResponse = 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方法
spring-cloud-netflix-eureka-client-2.0.0.RC1-sources.jar!/org/springframework/cloud/netflix/eureka/http/RestTemplateEurekaHttpClient.java
public EurekaHttpResponse<Void> cancel(String appName, String id) { String urlPath = serviceUrl + "apps/" + appName + '/' + id; ResponseEntity<Void> response = restTemplate.exchange(urlPath, HttpMethod.DELETE, null, Void.class); return anEurekaHttpResponse(response.getStatusCodeValue()) .headers(headersOf(response)).build(); } @Override public EurekaHttpResponse<Void> 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<Void> 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自動註冊,關閉的時候主要分兩大類:
EurekaRegistration是在close的時候,會觸發eurekaClient.shutdown方法 EurekaAutoServiceRegistration則在stop的時候,標記狀態爲DOWN,發佈StatusChangeEvent事件 EurekaClient則標註了@Bean(destroyMethod = "shutdown"),也就是在bean銷燬的時候,會觸發eurekaClient.shutdown方法
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幾類)。