SpringCloud源码阅读2-Eureka客户端的秘密(上)

简介: SpringCloud源码阅读2-Eureka客户端的秘密(上)

推荐阅读

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


相关文章
|
8天前
|
负载均衡 Java Nacos
SpringCloud基础1——远程调用、Eureka,Nacos注册中心、Ribbon负载均衡
微服务介绍、SpringCloud、服务拆分和远程调用、Eureka注册中心、Ribbon负载均衡、Nacos注册中心
SpringCloud基础1——远程调用、Eureka,Nacos注册中心、Ribbon负载均衡
|
27天前
|
Java Spring
【Azure Spring Cloud】Spring Cloud Azure 4.0 调用Key Vault遇见认证错误 AADSTS90002: Tenant not found.
【Azure Spring Cloud】Spring Cloud Azure 4.0 调用Key Vault遇见认证错误 AADSTS90002: Tenant not found.
|
27天前
|
Java Spring 容器
【Azure Spring Cloud】在Azure Spring Apps上看见 App Memory Usage 和 jvm.menory.use 的指标的疑问及OOM
【Azure Spring Cloud】在Azure Spring Apps上看见 App Memory Usage 和 jvm.menory.use 的指标的疑问及OOM
|
27天前
|
存储 Java Spring
【Azure Spring Cloud】Azure Spring Cloud服务,如何获取应用程序日志文件呢?
【Azure Spring Cloud】Azure Spring Cloud服务,如何获取应用程序日志文件呢?
|
27天前
|
SQL Java 数据库连接
【Azure Spring Cloud】Azure Spring Cloud connect to SQL using MSI
【Azure Spring Cloud】Azure Spring Cloud connect to SQL using MSI
|
27天前
|
Java 开发工具 Spring
【Azure Spring Cloud】使用azure-spring-boot-starter-storage来上传文件报错: java.net.UnknownHostException: xxxxxxxx.blob.core.windows.net: Name or service not known
【Azure Spring Cloud】使用azure-spring-boot-starter-storage来上传文件报错: java.net.UnknownHostException: xxxxxxxx.blob.core.windows.net: Name or service not known
|
28天前
|
NoSQL Java Redis
【Azure Spring Cloud】Java Spring Cloud 应用部署到Azure上后,发现大量的 java.lang.NullPointerException: null at io.lettuce.core.protocol.CommandHandler.writeSingleCommand(CommandHandler.java:426) at ... 异常
【Azure Spring Cloud】Java Spring Cloud 应用部署到Azure上后,发现大量的 java.lang.NullPointerException: null at io.lettuce.core.protocol.CommandHandler.writeSingleCommand(CommandHandler.java:426) at ... 异常
|
28天前
|
Java Spring
【Azure 应用服务】记一次Azure Spring Cloud 的部署错误 (az spring-cloud app deploy -g dev -s testdemo -n demo -p ./hellospring-0.0.1-SNAPSHOT.jar --->>> Failed to wait for deployment instances to be ready)
【Azure 应用服务】记一次Azure Spring Cloud 的部署错误 (az spring-cloud app deploy -g dev -s testdemo -n demo -p ./hellospring-0.0.1-SNAPSHOT.jar --->>> Failed to wait for deployment instances to be ready)
|
28天前
|
Java Maven Python
【Azure Spring Cloud】部署Azure spring cloud 失败
【Azure Spring Cloud】部署Azure spring cloud 失败
|
1月前
|
缓存 Java Maven
SpringCloud基于Eureka的服务治理架构搭建与测试:从服务提供者到消费者的完整流程
Spring Cloud微服务框架中的Eureka是一个用于服务发现和注册的基础组件,它基于RESTful风格,为微服务架构提供了关键的服务注册与发现功能。以下是对Eureka的详细解析和搭建举例。