Spring Cloud Eureka(五):Eureka Server 啟動流程分析


啟用EurekaServer

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

@EnableEurekaServer 源碼:

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

將配置類 EurekaServerMarkerConfiguration 加入spring 容器。

EurekaServerMarkerConfiguration 的源碼

/**
 *
 * 激活 EurekaServerAutoConfiguration 的開關
 */
@Configuration
public class EurekaServerMarkerConfiguration {

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

	class Marker {
	}
}

我們看到只是實例化了一個空類,沒有任何實現,從注釋中可以看到 EurekaServerMarkerConfiguration 是一個激活 EurekaServerAutoConfiguration 的開關。我們在項目源碼的 META-INF/spring.factories中看到

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

真正的配置信息在 EurekaServerAutoConfiguration 中,我們看到 @ConditionalOnBean(EurekaServerMarkerConfiguration.Marker.class) 只有存在 Marker 實例的時候,才會繼續加載配置項,這也就要求必須有 @EnableEurekaServer 注解,才能正常的啟動。

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

}

簡單的梳理一下 @EnableEurekaServer 的流程為:

import
掃描到
條件判斷
加載具體配置信息
EnableEurekaServer
EurekaServerMarkerConfiguration
EurekaServerAutoConfiguration
ConditionalOnBean
EurekaServerAutoConfiguration

EurekaServerAutoConfiguration 源碼

在源碼中我們可以看到一個很重要的注解 @Import(EurekaServerInitializerConfiguration.class) ,該注解是真正的的啟動了服務,下面我們就看一下它都有哪些操作

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

}

1、@Configuration 表明這是一個配置類
2、@Import(EurekaServerInitializerConfiguration.class) 導入啟動EurekaServer的bean
3、@ConditionalOnBean(EurekaServerMarkerConfiguration.Marker.class) 這個是表示只有在spring容器
里面含有Market這個實例的時候,才會加載當前這個Bean(EurekaServerAutoConfiguration ),這個就是控制
是否開啟EurekaServer的關鍵,

4、@EnableConfigurationProperties({ EurekaDashboardProperties.class, InstanceRegistryProperties.class })
加載配置
5、@PropertySource(“classpath:/eureka/server.properties”) 加載配置文件。

EurekaServerInitializerConfiguration 實現了SmartLifecycle.start方法,在spring 初始化的時候,會被調用

@Configuration
@CommonsLog
public class EurekaServerInitializerConfiguration
      implements ServletContextAware, SmartLifecycle, Ordered {
 
   @Autowired
   private EurekaServerConfig eurekaServerConfig;
 
   private ServletContext servletContext;
 
   @Autowired
   private ApplicationContext applicationContext;
 
   @Autowired
   private EurekaServerBootstrap eurekaServerBootstrap;
 
   private boolean running;
 
   private int order = 1;
 
   @Override
   public void setServletContext(ServletContext servletContext) {
      this.servletContext = servletContext;
   }
 
   @Override
   public void start() {
      // 啟動一個線程
      new Thread(new Runnable() {
         @Override
         public void run() {
            try {
               //初始化EurekaServer,同時啟動Eureka Server
               eurekaServerBootstrap.contextInitialized(EurekaServerInitializerConfiguration.this.servletContext);
               log.info("Started Eureka Server");
                // 發布EurekaServer的注冊事件
               publish(new EurekaRegistryAvailableEvent(getEurekaServerConfig()));
                // 設置啟動的狀態為true
               EurekaServerInitializerConfiguration.this.running = true;
                // 發布Eureka Start 事件,另外還有其他事件,我們可以根據需要監聽這些事件,然后做一些特定的業務需求
               publish(new EurekaServerStartedEvent(getEurekaServerConfig()));
            }
            catch (Exception ex) {
              
               log.error("Could not initialize Eureka servlet context", ex);
            }
         }
      }).start();
   }
 
   private EurekaServerConfig getEurekaServerConfig() {
      return this.eurekaServerConfig;
   }
 
   private void publish(ApplicationEvent event) {
      this.applicationContext.publishEvent(event);
   }
 
   @Override
   public void stop() {
      this.running = false;
      eurekaServerBootstrap.contextDestroyed(this.servletContext);
   }
 
  .................省略...............
}

eurekaServerBootstrap是spring cloud定義的類,其代碼完全拷貝了Eureka啟動類的實現。 這里看到在Eureka啟動后,會向外發送EurekaRegistryAvailableEvent 和 EurekaServerStartedEvent 事件,我們可以根據需要訂閱這兩個事件,做具體的處理操作。

接着我們一下 EurekaServerBootstrap 做了哪些具體的操作,從源碼中可以看到方法 contextInitialized(ServletContext context) 進行了環境和上下文的初始化工作,下面我們分別看一下:

EurekaServerBootstrap > contextInitialized

public void contextInitialized(ServletContext context) {
		try {
            //初始化執行環境
			initEurekaEnvironment();
            //初始化上下文
			initEurekaServerContext();

			context.setAttribute(EurekaServerContext.class.getName(), this.serverContext);
		}
		catch (Throwable e) {
			log.error("Cannot bootstrap eureka server :", e);
			throw new RuntimeException("Cannot bootstrap eureka server :", e);
		}
	}

EurekaServerBootstrap > contextInitialized > initEurekaEnvironment

protected void initEurekaEnvironment() throws Exception {
		log.info("Setting the eureka configuration..");

        //設置數據中心
		String dataCenter = ConfigurationManager.getConfigInstance()
				.getString(EUREKA_DATACENTER);
		if (dataCenter == null) {
			log.info(
					"Eureka data center value eureka.datacenter is not set, defaulting to default");
			ConfigurationManager.getConfigInstance()
					.setProperty(ARCHAIUS_DEPLOYMENT_DATACENTER, DEFAULT);
		}
		else {
			ConfigurationManager.getConfigInstance()
					.setProperty(ARCHAIUS_DEPLOYMENT_DATACENTER, dataCenter);
		}
        //設置 Eureka 環境
		String environment = ConfigurationManager.getConfigInstance()
				.getString(EUREKA_ENVIRONMENT);
		if (environment == null) {
			ConfigurationManager.getConfigInstance()
					.setProperty(ARCHAIUS_DEPLOYMENT_ENVIRONMENT, TEST);
			log.info(
					"Eureka environment value eureka.environment is not set, defaulting to test");
		}
		else {
			ConfigurationManager.getConfigInstance()
					.setProperty(ARCHAIUS_DEPLOYMENT_ENVIRONMENT, environment);
		}
	}

EurekaServerBootstrap > contextInitialized > initEurekaEnvironment

protected void initEurekaServerContext() throws Exception {
		// For backward compatibility
		JsonXStream.getInstance().registerConverter(new V1AwareInstanceInfoConverter(),
				XStream.PRIORITY_VERY_HIGH);
		XmlXStream.getInstance().registerConverter(new V1AwareInstanceInfoConverter(),
				XStream.PRIORITY_VERY_HIGH);

		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");

		// 從鄰近的eureka節點復制注冊表
		int registryCount = this.registry.syncUp();
		this.registry.openForTraffic(this.applicationInfoManager, registryCount);

		// 注冊所有監控統計信息
		EurekaMonitors.registerAllStats();
	}

至此,EurekaServer啟動完畢, EurekaServerBootstrap是完全復制了原生EurekaBootstrap的代碼, 因為原生的Eureka
是在servlet應用,但是spring cloud的應用是運行在內嵌的Tomcat等WEB服務器里面的,所以就使用EurekaServerBootstrap
來做替換,最終使Eureka能夠在spring boot中使用。


免責聲明!

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



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