推荐阅读
SpringCloud源码阅读0-SpringCloud必备知识
SpringCloud源码阅读1-EurekaServer源码的秘密
1.配置类
配置类的作用一般就是配置框架运行的基本组件,所以看懂配置类,也就入了框架的门。
当我们在启动类上加入@EnableDiscoveryClient
或者@EnableEurekaClient
时,就能使Eureka客户端生效。
这两个注解最终都会使,Eureka客户端对应的配置类EurekaClientAutoConfiguration
生效。这里直接讲配置类,具体注解如何使他生效,不在此处赘述。
EurekaClientAutoConfiguration 作为EurekaClient的自动配置类,配了EurekaClient运行所需要的组件。
(1.注解上的Bean
@Configuration @EnableConfigurationProperties//启动属性映射 @ConditionalOnClass(EurekaClientConfig.class)//需要EurekaClientConfig类存在 @Import(DiscoveryClientOptionalArgsConfiguration.class)//加载可选参数配置类到容器, @ConditionalOnBean(EurekaDiscoveryClientConfiguration.Marker.class)//需要开关存在 @ConditionalOnProperty(value = "eureka.client.enabled", matchIfMissing = true)//需要eureka.client.enabled属性 //在这三个自动注入类之前解析 @AutoConfigureBefore({ NoopDiscoveryClientAutoConfiguration.class, CommonsClientAutoConfiguration.class, ServiceRegistryAutoConfiguration.class }) //在这三个自动注入类之后解析 @AutoConfigureAfter(name = {"org.springframework.cloud.autoconfigure.RefreshAutoConfiguration", "org.springframework.cloud.netflix.eureka.EurekaDiscoveryClientConfiguration", "org.springframework.cloud.client.serviceregistry.AutoServiceRegistrationAutoConfiguration"}) public class EurekaClientAutoConfiguration { }
可以看出,注解大部分都是在做EurekaClientAutoConfiguration 配置类生效条件的判断。
其中@AutoConfigureAfter对应的三个配置类需要讲下: RefreshAutoConfiguration:与刷新作用域有关的配置类, EurekaDiscoveryClientConfiguration: * 向容器中注入开关EurekaDiscoveryClientConfiguration.Marker * 创建RefreshScopeRefreshedEvent事件监听器, * 当eureka.client.healthcheck.enabled=true时,注入EurekaHealthCheckHandler用于健康检查 AutoServiceRegistrationAutoConfiguration:关于服务自动注册的相关配置。
注解上没有引入重要组件。
(2.类内部的Bean
- HasFeatures: 特征类表示Eureka客户端的特征
- EurekaClientConfigBean: EurekaClient配置类,配置EurekaClient的相关信息。我们可以在配置文件中配置相关属性来覆盖默认的配置。
- ManagementMetadataProvider: 元数据管理类。
- EurekaInstanceConfigBean: EurekaInstance 配置类,配置当前Eureka客户端应用实例信息。列如,实例的hostname等,此类决定注册到Eureka注册中心的应用实例信息。
- DiscoveryClient: 注册一个(DiscoveryClient)EurekaDiscoveryClient. 用于从注册中心获取服务列表。注册此处的为org.springframework.cloud.client.discovery.DiscoveryClient
- EurekaServiceRegistry:Eureka服务注册器
- EurekaAutoServiceRegistration: Eureka服务自动注册器。此类实现了SmartLifecycle,也就是会在Spring容器启动后调用start()方法。说白了,他就是对EurekaServiceRegistry的进一步封装,提供自动注册的功能。由其参数(EurekaServiceRegistry registry,EurekaRegistration registration)可以看出他是通过EurekaServiceRegistry 向注册中心注册EurekaRegistration信息。
(3. 内部类的Bean EurekaClientAutoConfiguration 内部有两个关于EurekaClient的配置类,
- EurekaClientConfiguration: 普通EurekaClient配置
- RefreshableEurekaClientConfiguration:可刷新的EurekaClient配置
不管是哪种EurekaClient都会注册三个组件:
- EurekaClient: 注册一个(EurekaClient)CloudEurekaClient用于与Eureka Server进行交互,例如,注册,续约,下线等。
- ApplicationInfoManager: 管理并初始化当前Instance实例的注册信息,并提供了实例状态监听机制
- EurekaRegistration:Eureka实例的服务注册信息
(4. 分类总结:
- 通过EurekaClientConfigBean配置的属性,创建EurekaClient
- EurekaAutoServiceRegistration通过EurekaServiceRegistry注册器,注册注册EurekaRegistration信息
2.EurekaClient
EurekaClient 可以看做是客户端的上下文。他的初始化,卸载方法包括了客户端的整个生命周期
2.1EurekaClient构造方法
上文讲到,此时EurekaClient注册的是CloudEurekaClient。
@Inject DiscoveryClient(ApplicationInfoManager applicationInfoManager, EurekaClientConfig config, AbstractDiscoveryClientOptionalArgs args, Provider<BackupRegistry> backupRegistryProvider) { //留下扩展点,可以通过参数配置各种处理器 if (args != null) { this.healthCheckHandlerProvider = args.healthCheckHandlerProvider; this.healthCheckCallbackProvider = args.healthCheckCallbackProvider; this.eventListeners.addAll(args.getEventListeners()); this.preRegistrationHandler = args.preRegistrationHandler; } else { this.healthCheckCallbackProvider = null; this.healthCheckHandlerProvider = null; this.preRegistrationHandler = null; } //赋值applicationInfoManager属性 this.applicationInfoManager = applicationInfoManager; //applicationInfoManager在初始化时, //new InstanceInfoFactory().create(config);创建了当前实例信息 InstanceInfo myInfo = applicationInfoManager.getInfo(); clientConfig = config; staticClientConfig = clientConfig; transportConfig = config.getTransportConfig(); instanceInfo = myInfo; if (myInfo != null) { appPathIdentifier = instanceInfo.getAppName() + "/" + instanceInfo.getId(); } else { logger.warn("Setting instanceInfo to a passed in null value"); } //备用注册处理器 this.backupRegistryProvider = backupRegistryProvider; this.urlRandomizer = new EndpointUtils.InstanceInfoBasedUrlRandomizer(instanceInfo); //本地区域应用缓存初始化,Applications存储Application 列表 localRegionApps.set(new Applications()); fetchRegistryGeneration = new AtomicLong(0); remoteRegionsToFetch = new AtomicReference<String>(clientConfig.fetchRegistryForRemoteRegions()); remoteRegionsRef = new AtomicReference<>(remoteRegionsToFetch.get() == null ? null : remoteRegionsToFetch.get().split(",")); if (config.shouldFetchRegistry()) { this.registryStalenessMonitor = new ThresholdLevelsMetric(this, METRIC_REGISTRY_PREFIX + "lastUpdateSec_", new long[]{15L, 30L, 60L, 120L, 240L, 480L}); } else { this.registryStalenessMonitor = ThresholdLevelsMetric.NO_OP_METRIC; } if (config.shouldRegisterWithEureka()) { this.heartbeatStalenessMonitor = new ThresholdLevelsMetric(this, METRIC_REGISTRATION_PREFIX + "lastHeartbeatSec_", new long[]{15L, 30L, 60L, 120L, 240L, 480L}); } else { this.heartbeatStalenessMonitor = ThresholdLevelsMetric.NO_OP_METRIC; } //开始初始化默认区域 logger.info("Initializing Eureka in region {}", clientConfig.getRegion()); // 如果既不要向eureka server注册,又不要获取服务列表,就什么都不用初始化 if (!config.shouldRegisterWithEureka() && !config.shouldFetchRegistry()){ .... return; } try { //默认创建2个线程的调度池,用于TimedSupervisorTask任务。 scheduler = Executors.newScheduledThreadPool(2, new ThreadFactoryBuilder() .setNameFormat("DiscoveryClient-%d") .setDaemon(true) .build()); //心跳线程池,2个线程 heartbeatExecutor = new ThreadPoolExecutor( 1, clientConfig.getHeartbeatExecutorThreadPoolSize(), 0, TimeUnit.SECONDS, new SynchronousQueue<Runnable>(), new ThreadFactoryBuilder() .setNameFormat("DiscoveryClient-HeartbeatExecutor-%d") .setDaemon(true) .build() ); // use direct handoff //缓存刷新线程池,2个线程 cacheRefreshExecutor = new ThreadPoolExecutor( 1, clientConfig.getCacheRefreshExecutorThreadPoolSize(), 0, TimeUnit.SECONDS, new SynchronousQueue<Runnable>(), new ThreadFactoryBuilder() .setNameFormat("DiscoveryClient-CacheRefreshExecutor-%d") .setDaemon(true) .build() ); // use direct handoff //初始化与EurekaServer真正通信的通信器。 eurekaTransport = new EurekaTransport(); scheduleServerEndpointTask(eurekaTransport, args); //配置区域映射器。可配置DNS映射。 AzToRegionMapper azToRegionMapper; if (clientConfig.shouldUseDnsForFetchingServiceUrls()) { azToRegionMapper = new DNSBasedAzToRegionMapper(clientConfig); } else { azToRegionMapper = new PropertyBasedAzToRegionMapper(clientConfig); } if (null != remoteRegionsToFetch.get()) { azToRegionMapper.setRegionsToFetch(remoteRegionsToFetch.get().split(",")); } //创建实例区域检查器。 instanceRegionChecker = new InstanceRegionChecker(azToRegionMapper, clientConfig.getRegion()); } catch (Throwable e) { throw new RuntimeException("Failed to initialize DiscoveryClient!", e); } // 如果需要从eureka server获取服务列表,并且尝试fetchRegistry(false)失败, //调用BackupRegistry if (clientConfig.shouldFetchRegistry() && !fetchRegistry(false)) { fetchRegistryFromBackup(); } //回调扩展点处理器。此处理器,在注册前处理。 if (this.preRegistrationHandler != null) { this.preRegistrationHandler.beforeRegistration(); } if (clientConfig.shouldRegisterWithEureka() && clientConfig.shouldEnforceRegistrationAtInit()) { try { if (!register() ) { throw new IllegalStateException("Registration error at startup. Invalid server response."); } } catch (Throwable th) { logger.error("Registration error at startup: {}", th.getMessage()); throw new IllegalStateException(th); } } // 初始化所有定时任务 initScheduledTasks(); try { Monitors.registerObject(this); } catch (Throwable e) { logger.warn("Cannot register timers", e); } //将client,clientConfig放到DiscoveryManager 统一管理,以便其他地方可以DI依赖注入。 DiscoveryManager.getInstance().setDiscoveryClient(this); DiscoveryManager.getInstance().setEurekaClientConfig(config); }
Eureka初始化还是比较复杂,我们找重点说说。
2.1.1 EurekaTransport
在DiscoveryClient初始化时,初始化EurekaTransport。
eurekaTransport = new EurekaTransport(); scheduleServerEndpointTask(eurekaTransport, args);
EurekaTransport 是客户端与服务端底层通信器。 有5个重要的属性:
- transportClientFactory: 最底层http工具生产工厂。默认JerseyEurekaHttpClientFactory,即默认使用EurekaJerseyClient 通信的。
- newQueryClientFactory: 创建newQueryClient 的EurekaHttpClientFactory 工厂类
- newQueryClient: EurekaHttpClient类型http连接工具,负责获取Server端服务列表工作。
- registrationClientFactory: 创建registrationClient 的EurekaHttpClientFactory工厂类
- registrationClient : EurekaHttpClient 类型http连接工具,用于注册、续约相关工作。
scheduleServerEndpointTask方法完成EurekaTransport5个属性的初始化
transportClientFactory 属于低层次的http工厂。 EurekaHttpClientFactory 属于高层次的http工厂。 通过具有不同功能的EurekaHttpClientFactory 工厂 对transportClientFactory 进行层层装饰。生产的http工具也具有不同层次的功能。 列如: 最外层的SessionedEurekaHttpClient 具有会话功能的EurekaHttpClient 第二次RetryableEurekaHttpClient 具有重试功能的EurekaHttpClient