啟用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 的流程為:
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中使用。