首頁 > 軟體

Eureka原始碼閱讀解析Server伺服器端啟動流程範例

2022-10-18 14:02:46

環境

本文主要看看spring cloud是怎樣自動裝配eureka server 並啟動eureka server的並解析其關鍵原始碼。首先我們會設定啟動一個spring cloud eureka server 服務,接著看看是spring cloud怎樣自動裝配eureka的,並看看一些核心元件的建立原始碼,最後解析下eureka server 啟動初始化流程,看看都幹了些啥東西

1.spring cloud整合eureka server demo

1.1 新建spring boot專案

pom.xml檔案新增

用來規範spring cloud 的版本:

<dependencyManagement>
    <!--引入springcloud依賴的-->
    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-dependencies</artifactId>
            <version>2020.0.2E</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

新增用到的依賴:

<dependencies>
    <!--web依賴-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-commons</artifactId>
    </dependency>
    <!--unit test-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
    </dependency>
    <!--spring cloud eureka server 依賴-->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
    </dependency>
</dependencies>

組態檔

eureka:
  instance:
    hostname: localhost
  client:
    service-url:   #  eureka server 的地址, 咱們單範例模式就寫自己好了
      defaultZone:  http://localhost:7000/eureka
    register-with-eureka: false  # 不向eureka server 註冊自己
    fetch-registry: false  # 不向eureka server 獲取服務列表

1.2 啟動類

@SpringBootApplication
@EnableEurekaServer
public class EurekaServer {
    public static void main(String[] args) {
        SpringApplication.run(EurekaServer.class, args);
    }
}

新增@EnableEurekaServer註解,表示它是個eureka server

1.3 啟動

存取地址:http://localhost:7000/

注意:由於是原始碼解析的文章,更詳細的設定可以檢視github.com/hsfxuebao/s…

2. spring cloud自動裝配eureka server原始碼解析

2.1 @EnableEurekaServer註解

上一節設定eureka server的時候我們在設定類上加了個@EnableEurekaServer註解,表示啟動一個eureka server 。 我們看看這個註解

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(EurekaServerMarkerConfiguration.class)
public @interface EnableEurekaServer {
}

上面有個@Import(EurekaServerMarkerConfiguration.class) 註解匯入了EurekaServerMarkerConfiguration設定類,我們看下設定類:

@Configuration
public class EurekaServerMarkerConfiguration {
	@Bean
	public Marker eurekaServerMarkerBean() {
		return new Marker();
	}
	class Marker {
	}
}

就是建立了一個EurekaServerMarkerConfiguration.Marker 物件,交給spring保管,一看就是個標記,標識,僅僅是個標識。

2.2 EurekaServerAutoConfiguration

2.2.1 查詢starter 自動裝配類的技巧

spring boot的starter基本都會自動裝配類,也就是AutoConfiguration 結尾的類,如果我們找不到某個starter的自動裝配類是那個,可以去starter對應jar 包裡面META-INF 資料夾下面找spring.factories (這是spring spi 組態檔)檔案。比如說我可以在spring-cloud-netflix-eureka-server 這個專案下找到META-INF 資料夾下面找spring.factories檔案。

檔案內容就是上圖裡面的,可以看到自動裝配類就是EurekaServerAutoConfiguration

2.2.2 EurekaServerAutoConfiguration原始碼解析

首先,看看 EurekaServerAutoConfiguration 自動設定類裡面都幹了啥。

@Configuration
@Import(EurekaServerInitializerConfiguration.class)
@ConditionalOnBean(EurekaServerMarkerConfiguration.Marker.class)
@EnableConfigurationProperties({ EurekaDashboardProperties.class,
		InstanceRegistryProperties.class })
@PropertySource("classpath:/eureka/server.properties")
public class EurekaServerAutoConfiguration extends WebMvcConfigurerAdapter{}

先看看類上面宣告的這一堆註解:

  • @Import(EurekaServerInitializerConfiguration.class) : 匯入(裝配)設定類EurekaServerInitializerConfiguration,這個我們後面看下,屬於初始化流程。
  • @ConditionalOnBean(EurekaServerMarkerConfiguration.Marker.class) 就是是否存在EurekaServerMarkerConfiguration.Marker 類的bean範例,這個肯定存在的,我們在@EnableEurekaServer 註解中就建立了這麼一個bean 交給spring 管理了。存在就會往下走,不存在就拉倒了,不會再載入設定類裡面別的東西了,也就是不會自動啟動eureka server了。
  •  @EnableConfigurationProperties({ EurekaDashboardProperties.class, InstanceRegistryProperties.class }) 這個就是裝配這兩個設定類,一個是關於Dashboard 的,一個是關於instance 的設定,也就是eureka server的設定, 就是將我們寫在application.yml組態檔中關於eureka 的設定set到這裡面的屬性中。
  • @PropertySource(“classpath:/eureka/server.properties”) 載入/eureka/server.properties 組態檔,這個沒啥意思。

然後,看看裡面成員有哪些。

@Autowired
private ApplicationInfoManager applicationInfoManager;
@Autowired
private EurekaServerConfig eurekaServerConfig;
@Autowired
private EurekaClientConfig eurekaClientConfig;
@Autowired
private EurekaClient eurekaClient;
@Autowired
private InstanceRegistryProperties instanceRegistryProperties;

ApplicationInfoManager / EurekaClientConfig / EurekaClient 這幾個bean 一看就是eureka client 裡面的,我們這裡是eureka server ,為啥能夠注入進來呢?

  • 原因是 eureka server 的starter 裡面依賴了eureka client starter,eureka client starter自動裝配的這些範例bean 。有些小夥伴可能會有疑問,我啟用(自動裝配)eureka client,不需要在設定類上面新增@EnableEurekaClient註解嘛。其實從spring cloud netflix 1.4版本往後主要引入了這個 eureka client starter 依賴,就會自動裝配,不需要新增@EnableEurekaClient 註解也是可以的。

接著,看看建立了哪些核心元件

dashboard 的controller

@Bean
@ConditionalOnProperty(prefix = "eureka.dashboard", name = "enabled", matchIfMissing = true)
public EurekaController eurekaController() {
return new EurekaController(this.applicationInfoManager);
}

登入檔建立

@Bean
public PeerAwareInstanceRegistry peerAwareInstanceRegistry(
                ServerCodecs serverCodecs) {
        this.eurekaClient.getApplications(); // force initialization
        return new InstanceRegistry(this.eurekaServerConfig, this.eurekaClientConfig,
                        serverCodecs, this.eurekaClient,
                        this.instanceRegistryProperties.getExpectedNumberOfClientsSendingRenews(),
                        this.instanceRegistryProperties.getDefaultOpenForTrafficCount());
}

登入檔,就是存放我們註冊的範例資訊的,這個是個非常核心的元件。這裡直接建立了一個InstanceRegistry 物件,這個InstanceRegistry繼承eureka server 裡面PeerAwareInstanceRegistryImpl 類。

叢集節點資訊類物件

@Bean
@ConditionalOnMissingBean
public PeerEurekaNodes peerEurekaNodes(PeerAwareInstanceRegistry registry,
                ServerCodecs serverCodecs) {
        return new RefreshablePeerEurekaNodes(registry, this.eurekaServerConfig,
                        this.eurekaClientConfig, serverCodecs, this.applicationInfoManager);
}

PeerEurekaNodes 這個類,從名字上也能看出來是叢集節點的類,而且是Nodes ,表示多個節點,其實裡面就是封裝eureka server 叢集的節點們。這裡是建立RefreshablePeerEurekaNodes 物件,RefreshablePeerEurekaNodes繼承PeerEurekaNodes 類,實現spring 的ApplicationListener 事件監聽介面,自然而然的RefreshablePeerEurekaNodes類具有了重新整理叢集節點資訊功能(能力)。

EurekaServerContext

@Bean
public EurekaServerContext eurekaServerContext(ServerCodecs serverCodecs,
                PeerAwareInstanceRegistry registry, PeerEurekaNodes peerEurekaNodes) {
        return new DefaultEurekaServerContext(this.eurekaServerConfig, serverCodecs,
                        registry, peerEurekaNodes, this.applicationInfoManager);
}

EurekaServerContext 從字面上是eureka server 上下文,其實就是eureka server 啟動的時候會初始化PeerEurekaNodesPeerAwareInstanceRegistry 登入檔,銷燬的是否停止這2個元件

EurekaServerBootstrap

@Bean
public EurekaServerBootstrap eurekaServerBootstrap(PeerAwareInstanceRegistry registry,
                EurekaServerContext serverContext) {
        return new EurekaServerBootstrap(this.applicationInfoManager,
                        this.eurekaClientConfig, this.eurekaServerConfig, registry,
                        serverContext);
}

一般叫Bootstrap 的類都是那種掌管專案啟動與停止的,EurekaServerBootstrap 也不例外,它裡面就有contextInitialized 專案啟動與contextDestroyed專案停止/銷燬的方法。

jersey框架 關於jersey框架的東西,eureka server 對外暴露rest ful介面,使用的框架就是jersey(這玩意國內很少用),作用/定位就跟我們常用的springmvc 框架差不多。

3. eureka server 初始化原始碼解析

3.1 EurekaServerInitializerConfiguration

第2小節介紹 spring cloud eureka server 啟動的時候自動裝配的一些元件,本小節就看下eureka server的初始化流程。

在自動裝配類EurekaServerAutoConfiguration 類上面宣告了@Import(EurekaServerInitializerConfiguration.class) ,我們介紹說是匯入(載入)EurekaServerInitializerConfiguration 這個設定,一看這個類名字就是eureka server 初始化用的。

@Configuration
public class EurekaServerInitializerConfiguration
		implements ServletContextAware, SmartLifecycle, Ordered {
}

@Configuration是個設定類,實現ServletContextAware,SmartLifecycle,Ordered 介面:

  • ServletContextAware 作用是自動注入ServletContext 範例。
  • SmartLifecycle介面 屬於spring 宣告週期的,start 方法執行時機是,當spring 的bean都範例化,初始化完事後,會呼叫SmartLifecycle 的start方法,isAutoStartup 方法返回的true 或者false 決定要不要執行start方法,這裡isAutoStartup方法返回的true。
  • stop 方法就是專案停止,容器銷燬的時候會呼叫。

先來看下start 方法幹了啥

@Override
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();
}

建立了一個執行緒,先是執行eurekaServerBootstrapcontextInitialized 初始化方法。接著就是釋出 EurekaRegistryAvailableEvent 事件,設定running 為true ,最後就是釋出EurekaServerStartedEvent 事件。 這裡我們只關心eurekaServerBootstrap的contextInitialized 方法。

public void contextInitialized(ServletContext context) {
	initEurekaEnvironment();
	initEurekaServerContext();
	context.setAttribute(EurekaServerContext.class.getName(), this.serverContext);
}

初始化eureka environment, 接著初始化context,最後是將context塞到 ServletContext的屬性中。 initEurekaEnvironment這個我們就不看了。主要看下initEurekaServerContext 方法:

if (isAws(this.applicationInfoManager.getInfo())) {
	this.awsBinder = new AwsBinderDelegate(this.eurekaServerConfig,
			this.eurekaClientConfig, this.registry, this.applicationInfoManager);
	this.awsBinder.start();
}
EurekaServerContextHolder.initialize(this.serverContext);
log.info("Initialized server context");
// Copy registry from neighboring eureka node
int registryCount = this.registry.syncUp();
this.registry.openForTraffic(this.applicationInfoManager, registryCount);
// Register all monitoring statistics.
EurekaMonitors.registerAllStats();

isAws 是亞馬遜雲環境的時候執行if裡面的那一堆,這個不看了。

EurekaServerContextHolder.initialize(this.serverContext);這行程式碼沒幹啥。

int registryCount = this.registry.syncUp();
this.registry.openForTraffic(this.applicationInfoManager, registryCount);

registry.syncUp()這個就是去叢集中的其他節點拉取登入檔。下篇文章分析 registry.openForTraffic()這個是Server端定時清理過期的Client。以後在詳細分析。

3.2 DefaultEurekaServerContext

我們在介紹自動裝配類EurekaServerAutoConfiguration 裝配元件的時候,介紹過EurekaServerContext ,說他字面上是eureka server 上下文,其實就是eureka server 啟動的時候會初始化PeerEurekaNodes 與PeerAwareInstanceRegistry 登入檔,銷燬的是否停止這2個元件 我們來看下 @PostConstruct 註解修飾的initialize 方法

@PostConstruct
public void initialize() {
    this.peerEurekaNodes.start();
    this.registry.init(this.peerEurekaNodes);
}

幹了2件事,啟動peerEurekaNodes ,初始化登入檔。 peerEurekaNodes#start:

public void start() {
     updatePeerEurekaNodes(resolvePeerUrls());
     Runnable peersUpdateTask = new Runnable() {
         @Override
         public void run() {
             updatePeerEurekaNodes(resolvePeerUrls());
         }
     };
     taskExecutor.scheduleWithFixedDelay(
             peersUpdateTask,
             serverConfig.getPeerEurekaNodesUpdateIntervalMs(),
             serverConfig.getPeerEurekaNodesUpdateIntervalMs(),
             TimeUnit.MILLISECONDS
     );
}

先是更新叢集節點資訊,就是建立節點物件。

接著就是建立一個定時任務更新,預設是10分鐘。

登入檔初始化:

@Override
public void init(PeerEurekaNodes peerEurekaNodes) throws Exception {
    this.numberOfReplicationsLastMin.start();
    this.peerEurekaNodes = peerEurekaNodes;
    initializedResponseCache();
    scheduleRenewalThresholdUpdateTask();
    initRemoteRegionRegistry();
}
  • numberOfReplicationsLastMin 這是記錄續約次數的一個元件,用在服務剔除。
  • initializedResponseCache 初始化響應cache ,這個其實就是 服務發現的時候三級快取。

參考

eureka-0.10.11原始碼(註釋)

springcloud-source-study學習github地址

以上就是Eureka原始碼閱讀解析Server伺服器端啟動流程範例的詳細內容,更多關於Eureka Server伺服器端啟動流程的資料請關注it145.com其它相關文章!


IT145.com E-mail:sddin#qq.com