spring cloud服务发现和注册源码分析

概述

Spring Cloud中默认的服务发现采用的netflix的eureka,本篇文章就是阅读Spring cloud中通过eureka做服务发现时的笔记。

顺藤摸瓜

读取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 EnableDiscoveryClientImportSelectorextends SpringFactoryImportSelector {@Overridepublic String[] selectImports(AnnotationMetadata metadata) {...boolean autoRegister = attributes.getBoolean("autoRegister");if (autoRegister) {List 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这个类,再看一下这个类

@Configuration@EnableConfigurationProperties(AutoServiceRegistrationProperties.class)public class AutoServiceRegistrationConfiguration {}

什么都没有,怎么回事。其实机关就是在SpringFactoryImportSelector这个类。其实他会把类全命名作为自动配置类的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的类:EurekaClientAutoConfiguration

@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@Lazypublic EurekaClient eurekaClient(ApplicationInfoManager manager, EurekaClientConfig config, EurekaInstanceConfig instance) {manager.getInfo(); // force initializationreturn new CloudEurekaClient(manager, config, this.optionalArgs,this.context);}

spring cloud服务发现和注册源码分析

终于发现和NetFlix的链接的地方,CloudEurekaClient继承的DiscoveryClient其实就是NetFlix的服务发现类,这样,就可以好好分析是怎么初始化的。

直击要害

CloudEurekaClient在初始化的时候主要是调用父类DiscoveryClient的构造函数,会做很多事,其中注册服务和发现服务是通过调度任务来完成,调度任务的初始化是在initScheduledTasks这个而方法中,其中服务发现的代码

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服务器交互。服务注册的代码紧接着在服务发现后面

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 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 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进行通信,实现手段则是通过定时任务进行定时操作,包括定时查询,服务续约。

tencent://AddContact/?fromId=50&fromSubId=1&subcmd=all&uin=2284732365


分享到:


相關文章: