Eureka 系列(03)Spring Cloud 自動裝配原理


Eureka 系列(03)Spring Cloud 自動裝配原理

0. Spring Cloud 系列目錄 - Eureka 篇

本文主要是分析 Spring Cloud 是如何整合 Eureka 的,但不會具體分析 Eureka 的源碼,之后的文章會對 Eureka 的源碼做一個比較具體的分析。

1. Eureka Client 自動裝配

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.cloud.netflix.eureka.EurekaClientAutoConfiguration,\
org.springframework.cloud.netflix.eureka.EurekaDiscoveryClientConfiguration
圖1:Eureka Client 自動裝配時序圖
sequenceDiagram participant EurekaClientAutoConfiguration participant EurekaDiscoveryClientConfiguration participant CloudEurekaClient participant EurekaAutoServiceRegistration EurekaClientAutoConfiguration ->> CloudEurekaClient : @Bean EurekaDiscoveryClientConfiguration ->> EurekaAutoServiceRegistration : @Bean EurekaAutoServiceRegistration ->> CloudEurekaClient : register

總結: Eureka Client 的裝配很簡單,主要是組裝 DiscoveryClient。

  • EurekaClientAutoConfiguration 主要是裝配 EurekaClient
  • EurekaDiscoveryClientConfiguration 主要是在 Eureka Client 啟動時立即將自身注冊到 Eureka Server 上。

1.1 裝配 DiscoveryClient

EurekaClientAutoConfiguration 主要是裝配 CloudEurekaClient,CloudEurekaClient 繼承了 DiscoveryClient,主要是增加了 Spring 的事件機制。

@Bean(destroyMethod = "shutdown")
public EurekaClient eurekaClient(ApplicationInfoManager manager,
	EurekaClientConfig config, EurekaInstanceConfig instance,
	@Autowired(required = false) HealthCheckHandler healthCheckHandler) {
    CloudEurekaClient cloudEurekaClient = new CloudEurekaClient(
        appManager, config, this.optionalArgs, this.context);
    cloudEurekaClient.registerHealthCheck(healthCheckHandler);
    return cloudEurekaClient;
}

1.2 啟動時立即注冊

EurekaDiscoveryClientConfiguration 主要是實現了自動注冊。在 DiscoveryClient 中默認是啟動 40s 后才會注冊,延遲太長,Spring Cloud 改變了這種默認實現,在啟動時調用 EurekaAutoServiceRegistration.start() ,將自身實例注冊到 Eureka Server 上。

// EurekaAutoServiceRegistration 啟動時自動注冊
public void start() {
    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);
    }
}

1.3 替換 EurekaHttpClient

Spring Cloud 實現了自己的 RestTemplateEurekaHttpClient,可以替換默認的 JerseyApplicationClient。DiscoveryClientOptionalArgsConfiguration 中裝配條件如下:

@Bean
@ConditionalOnMissingClass("com.sun.jersey.api.client.filter.ClientFilter")
@ConditionalOnMissingBean(value = AbstractDiscoveryClientOptionalArgs.class, search = SearchStrategy.CURRENT)
public RestTemplateDiscoveryClientOptionalArgs restTemplateDiscoveryClientOptionalArgs() {
    return new RestTemplateDiscoveryClientOptionalArgs();
}

查一下 com.sun.jersey 的依賴情況,可以看到 eureka-client 默認會引入 jersey-client,也就是說會使用默認的 JerseyApplicationClient。

>mvn dependency:tree -Dincludes="com.sun.jersey"
[INFO] com.github.binarylei.springcloud:user-consumer-eureka-client:jar:1.0.0
[INFO] \- org.springframework.cloud:spring-cloud-starter-netflix-eureka-client:jar:2.1.1.RELEASE:compile
[INFO]    \- com.netflix.eureka:eureka-client:jar:1.9.8:compile
[INFO]       +- com.sun.jersey:jersey-core:jar:1.19.1:runtime
[INFO]       \- com.sun.jersey:jersey-client:jar:1.19.1:runtime

既然知道了原因,要替換為 RestTemplateEurekaHttpClient 就很簡單了。

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
    <exclusions>
        <exclusion>
            <groupId>com.sun.jersey</groupId>
            <artifactId>jersey-client</artifactId>
        </exclusion>
    </exclusions>
</dependency>

分析了客戶自動裝配后,接下來繼續分析 Eureka Server 服務端的啟動原理,Eureka 服務端的啟動同樣依賴 DiscoverClient(個人感覺依賴有點混亂)。

2. Eureka Server 自動裝配

在原生的 Eureka 中服務端的啟動類是 EurekaBootStrap,Spring Cloud 中啟動類是 EurekaServerBootstrap,原理大致都差不多。本文會以 EurekaServerBootstrap 為切入點進行分析。

EurekaServerAutoConfiguration 是 Spring Cloud 自動裝配入口,配置如下:

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
  org.springframework.cloud.netflix.eureka.server.EurekaServerAutoConfiguration

注意: EurekaServerAutoConfiguration 需要配合 @EnableEurekaServer 使用。

2.1 Eureka Server 自動裝配流程

圖2:Eureka Server 自動裝配時序圖
sequenceDiagram participant EurekaServerAutoConfiguration participant EurekaServerInitializerConfiguration participant PeerAwareInstanceRegistry participant PeerEurekaNodes participant EurekaServerBootstrap participant EurekaClient EurekaServerAutoConfiguration ->> PeerAwareInstanceRegistry : @Bean EurekaServerAutoConfiguration ->> PeerEurekaNodes : @Bean EurekaServerAutoConfiguration ->> EurekaServerBootstrap : @Bean EurekaServerAutoConfiguration ->> EurekaServerInitializerConfiguration : @Import loop Eureka Server 服務啟動 EurekaServerInitializerConfiguration ->> EurekaServerInitializerConfiguration : start EurekaServerInitializerConfiguration ->> EurekaServerBootstrap : contextInitialized EurekaServerBootstrap ->> PeerAwareInstanceRegistry : 1. 從其它服務器同步數據:syncUp PeerAwareInstanceRegistry ->> EurekaClient : 獲取其它服務器的數據:getApplications EurekaServerBootstrap ->> PeerAwareInstanceRegistry : 2. 啟動自動過期定時任務:openForTraffic end loop Eureka Server 服務銷毀 EurekaServerInitializerConfiguration ->> EurekaServerInitializerConfiguration : stop EurekaServerInitializerConfiguration ->> EurekaServerBootstrap : contextDestroyed end

總結: Spring Cloud 有個特點,一般來說類的職責都很明確。

  1. EurekaServerAutoConfiguration 主要是裝配 EurekaServerBootstrap
  2. EurekaServerInitializerConfiguration 則負責啟動 EurekaServerBootstrap
  3. EurekaServerBootstrap 啟動時主要完成兩件事:一是從其它 Eureka Server 上同步數據;二是啟動自動過期定時任務 EvictionTask。可以看到 Eureka 最核心的數據結構是 PeerAwareInstanceRegistry。

Eureka Server 核心實現類:

  • PeerAwareInstanceRegistry 負責 Eureka Server 之間數據同步,其父類 AbstractInstanceRegistry 則管理所有的本地注冊信息。
  • PeerEurekaNodes 負責 Eureka Server 服務器列表管理。
  • EurekaServerBootstrap 啟動類。
  • EurekaClient 上文提到 Eureka Server 啟動是要依賴 Eureka Client 客戶端,所以也會自動裝配 EurekaClient,啟動時同步數據會依賴 EurekaClient。

2.2 EurekaServerBootstrap

EurekaServerInitializerConfiguration 實現了 SmartLifecycle 接口,也就是啟動和銷毀時會分別調用 start 和 stop 方法。

public void start() {
    new Thread(new Runnable() {
        @Override
        public void run() {
            try {
                // TODO: is this class even needed now?
                eurekaServerBootstrap.contextInitialized(
                    EurekaServerInitializerConfiguration.this.servletContext);
                log.info("Started Eureka Server");

                publish(new EurekaRegistryAvailableEvent(getEurekaServerConfig()));
                EurekaServerInitializerConfiguration.this.running = true;
                publish(new EurekaServerStartedEvent(getEurekaServerConfig()));
            }
            catch (Exception ex) {
                // Help!
                log.error("Could not initialize Eureka servlet context", ex);
            }
        }
    }).start();
}

public void stop() {
    this.running = false;
    eurekaServerBootstrap.contextDestroyed(this.servletContext);
}

繼續關注 EurekaServerBootstrap 的 contextInitialized 和 contextDestroyed 方法分別完成了什么事情。

public void contextInitialized(ServletContext context) {
    initEurekaEnvironment();
    initEurekaServerContext();
}

protected void initEurekaServerContext() throws Exception {
    ...
    // Copy registry from neighboring eureka node
    int registryCount = this.registry.syncUp();
    this.registry.openForTraffic(this.applicationInfoManager, registryCount);

    // Register all monitoring statistics.
    EurekaMonitors.registerAllStats();
}

總結: 可以看到 EurekaServerBootstrap 啟動時主要步驟:一是同步數據;二是啟動定時過期的任務 EvictionTask。具體的源碼會在之后分析。


每天用心記錄一點點。內容也許不重要,但習慣很重要!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM