Spring Cloud中默认的服务发现采用的netflix的eureka,本篇文章就是阅读Spring cloud中经过eureka作服务发现时的笔记。spring
读取Spring各类框架的时候,不少时候不知道从什么地方开始,由于Spring中不少模块的开启就是经过一行注解,例如@EnableXXX。而Spring Cloud中的配置服务则是经过@EnableDiscoveryClient,(其实@EnableEurekaClient就是@EnableDiscoveryClient)缓存
/** * Annotation to enable a DiscoveryClient implementation. * @author Spencer Gibb */ @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @Import(EnableDiscoveryClientImportSelector.class) public @interface EnableDiscoveryClient { /** * If true, the ServiceRegistry will automatically register the local server. */ boolean autoRegister() default true; }
上面有一个@Import(EnableDiscoveryClientImportSelector.class),而后看一下EnableDiscoveryClientImportSelector这个类服务器
public class EnableDiscoveryClientImportSelector extends SpringFactoryImportSelector<EnableDiscoveryClient> { @Override public String[] selectImports(AnnotationMetadata metadata) { ... boolean autoRegister = attributes.getBoolean("autoRegister"); if (autoRegister) { List<String> importsList = new ArrayList<>(Arrays.asList(imports)); importsList.add("org.springframework.cloud.client.serviceregistry.AutoServiceRegistrationConfiguration"); imports = importsList.toArray(new String[0]); } return imports; } }
一看代码第一反应就是引入了org.springframework.cloud.client.serviceregistry.AutoServiceRegistrationConfiguration这个类,再看一下这个类app
@Configuration @EnableConfigurationProperties(AutoServiceRegistrationProperties.class) public class AutoServiceRegistrationConfiguration { }
什么都没有,怎么回事。其实机关就是在SpringFactoryImportSelector<EnableDiscoveryClient>这个类。其实他会把类全命名做为自动配置类的key,了解Spring Boot自动加载过程的就应该知道在某个jar包下META-INFO/spring.factories。框架
org.springframework.cloud.client.discovery.EnableDiscoveryClient=\ org.springframework.cloud.netflix.eureka.EurekaDiscoveryClientConfiguration
这样,Spring Cloud启动的时候初始化Eureka就是在EurekaDiscoveryClientConfiguration这个类中。仔细一看,也不对啊,这个类只注册了一个Marker类,并且这个类是个空类。再全局搜索一下哪里引用了这个Marker类,终于发现真正初始化Eureka的类:EurekaClientAutoConfigurationide
@Configuration @EnableConfigurationProperties @ConditionalOnClass(EurekaClientConfig.class) @ConditionalOnBean(EurekaDiscoveryClientConfiguration.Marker.class) @ConditionalOnProperty(value = "eureka.client.enabled", matchIfMissing = true) @AutoConfigureBefore({ NoopDiscoveryClientAutoConfiguration.class, CommonsClientAutoConfiguration.class, ServiceRegistryAutoConfiguration.class }) @AutoConfigureAfter(name = "org.springframework.cloud.autoconfigure.RefreshAutoConfiguration") public class EurekaClientAutoConfiguration
既然找到了EurekaClientAutoConfiguration这个配置类,那么确定有初始化和NetFlix相关类,仔细一看,就是函数
@Bean(destroyMethod = "shutdown") @ConditionalOnMissingBean(value = EurekaClient.class, search = SearchStrategy.CURRENT) @org.springframework.cloud.context.config.annotation.RefreshScope @Lazy public EurekaClient eurekaClient(ApplicationInfoManager manager, EurekaClientConfig config, EurekaInstanceConfig instance) { manager.getInfo(); // force initialization return new CloudEurekaClient(manager, config, this.optionalArgs, this.context); }
终于发现和NetFlix的连接的地方,CloudEurekaClient继承的DiscoveryClient其实就是NetFlix的服务发现类,这样,就能够好好分析是怎么初始化的。oop
CloudEurekaClient在初始化的时候主要是调用父类DiscoveryClient的构造函数,会作不少事,其中注册服务和发现服务是经过调度任务来完成,调度任务的初始化是在initScheduledTasks这个而方法中,其中服务发现的代码this
ate void initScheduledTasks() { if (clientConfig.shouldFetchRegistry()) { // registry cache refresh timer int registryFetchIntervalSeconds = clientConfig.getRegistryFetchIntervalSeconds(); int expBackOffBound = clientConfig.getCacheRefreshExecutorExponentialBackOffBound(); scheduler.schedule( new TimedSupervisorTask( "cacheRefresh", scheduler, cacheRefreshExecutor, registryFetchIntervalSeconds, TimeUnit.SECONDS, expBackOffBound, new CacheRefreshThread() ), registryFetchIntervalSeconds, TimeUnit.SECONDS); }
首先判断是否须要进行服务发现,而后经过一个定时任务去刷新缓存信息。TimedSupervisorTask是支持timeout的调度任务,刷新缓存逻辑实际是在CacheRefreshThread()中,日后看就会看到经过Http请求和Erureka服务器交互。服务注册的代码紧接着在服务发现后面url
if (clientConfig.shouldRegisterWithEureka()) { ... // Heartbeat timer scheduler.schedule( new TimedSupervisorTask( "heartbeat", scheduler, heartbeatExecutor, renewalIntervalInSecs, TimeUnit.SECONDS, expBackOffBound, new HeartbeatThread() ), renewalIntervalInSecs, TimeUnit.SECONDS); // InstanceInfo replicator instanceInfoReplicator = new InstanceInfoReplicator( this, instanceInfo, clientConfig.getInstanceInfoReplicationIntervalSeconds(), 2); // burstSize ... if (clientConfig.shouldOnDemandUpdateStatusChange()) { applicationInfoManager.registerStatusChangeListener(statusChangeListener); } instanceInfoReplicator.start(clientConfig.getInitialInstanceInfoReplicationIntervalSeconds()); }
instanceInfoReplicator对当前服务信息( instanceInfo)进行注册。
public void run() { ... discoveryClient.refreshInstanceInfo(); Long dirtyTimestamp = instanceInfo.isDirtyWithTime(); if (dirtyTimestamp != null) { discoveryClient.register(); instanceInfo.unsetIsDirty(dirtyTimestamp); } ... }
另一个心跳检测的定时任务则是对服务进行续约
boolean renew() { EurekaHttpResponse<InstanceInfo> httpResponse; try { httpResponse = eurekaTransport.registrationClient.sendHeartBeat(instanceInfo.getAppName(), instanceInfo.getId(), instanceInfo, null); if (httpResponse.getStatusCode() == 404) { ... return register(); } return httpResponse.getStatusCode() == 200; } catch (Throwable e) { logger.error("{} - was unable to send heartbeat!", PREFIX + appPathIdentifier, e); return false; } }
能够看出若是续约失败则从新发起一次注册。那么服务注册的具体实现就在register()中
boolean register() throws Throwable { EurekaHttpResponse<Void> httpResponse; try { httpResponse = eurekaTransport.registrationClient.register(instanceInfo); } catch (Exception e) { logger.warn("{} - registration failed {}", PREFIX + appPathIdentifier, e.getMessage(), e); throw e; } if (logger.isInfoEnabled()) { logger.info("{} - registration status: {}", PREFIX + appPathIdentifier, httpResponse.getStatusCode()); } return httpResponse.getStatusCode() == 204; }
实现十分简单,就是发起一次http调用,把当前instanceInfo传递过去。,那么InstanceInfo都有什么呢,其实就是一些appName ,url,port等等,经过这些信息咱们就能发起一次基于http的rpc调用。
能够看出,Spring cloud中的服务发现和注册其实就是和经过http方式和eureka进行通讯,实现手段则是经过定时任务进行定时操做,包括定时查询,服务续约。