1.入口EurekaServerAutoConfiguration
在第一节中,我们在启动类中使用EnableEurekaServer
标注该项目为Eureka注册中心服务端,而该类是在org.springframework.cloud:spring-cloud-netflix-eureka-server
依赖包下,那么根据springboot
的自动装配原理 ,我们在META-INF
文件夹下的spring.factoryies下发现,自动装配类为org.springframework.cloud.netflix.eureka.server.EurekaServerAutoConfiguration
那么一切的故事将这个类开始。
2.EurekaServerAutoConfiguration
装配前提条件
在EurekaServerAutoConfiguration
的注解中,存在ConditionalOnBean
注解标注:只有当满足存在
EurekaServerMarkerConfiguration.Marker
时,才会去自动装配。
而我们可以看到EurekaServerMarkerConfiguration
是被@Configuration注解,通过一下代码进行实例化Marker对象
@Bean
public Marker eurekaServerMarkerBean() {
return new Marker();
}
但是未被扫描的EurekaServerMarkerConfiguration
如何进行实例化呢?我们在EnableEurekaServer
注解上看到Import
注解中中标注了,EnableEurekaServer使用时,需要先实例化EurekaServerMarkerConfiguration
到容器管理中。由此可见,我们为啥要在启动类上加上EnableEurekaServer
注解了。
Import的的作用请参照:Spring-Import注解
3.EurekaServerInitializerConfiguration初始化
EurekaServerAutoConfiguration
上通过Import
引入了EurekaServerInitializerConfiguration
实例。那我们先解析一下EurekaServerInitializerConfiguration
源码
/**
* 1.EurekaServerInitializerConfiguration 实现了ServletContextAware,代表着会自动注入ServletContext实例到该类中(EurekaServer启动需要)
* 2.SmartLifecycle 是一个接口。当Spring容器加载所有bean并完成初始化之后,会接着回调实现该接口的类中对应的start()方法(参照org.springframework.context.support.DefaultLifecycleProcessor#doStart(java.util.Map, java.lang.String, boolean))
*/
@Configuration
public class EurekaServerInitializerConfiguration
implements ServletContextAware, SmartLifecycle, Ordered {
private static final Log log = LogFactory.getLog(EurekaServerInitializerConfiguration.class);
/**
* eureka 服务端配置
*/
@Autowired
private EurekaServerConfig eurekaServerConfig;
/**
* 通过ServletContextAware注入的servletContext
*/
private ServletContext servletContext;
/**
* 通过Autowired自动注入ApplicationContext
*/
@Autowired
private ApplicationContext applicationContext;
/**
* 见名知意,Eureka服务端的启动引导类
*/
@Autowired
private EurekaServerBootstrap eurekaServerBootstrap;
private boolean running;
private int order = 1;
@Override
public void setServletContext(ServletContext servletContext) {
this.servletContext = servletContext;
}
/**
* 当Spring容器加载所有bean并完成初始化之后,会接着回调SmartLifecycle中的start方法
* 参照org.springframework.context.support.DefaultLifecycleProcessor#doStart(java.util.Map, java.lang.String, boolean)
*
*/
@Override
public void start() {
new Thread(new Runnable() {
@Override
public void run() {
try {
//TODO: is this class even needed now?
// eureka服务端启动引导类开始上下文初始化 (重点关注)
eurekaServerBootstrap.contextInitialized(EurekaServerInitializerConfiguration.this.servletContext);
// 清晰明了的日志告诉我们,Eureka Server 启动了。所以上面那个就是Eureka启动的核心步骤
// 我们可以慢慢解析
log.info("Started Eureka Server");
// 发布 Eureka 注册可用事件 (暂不关注)
publish(new EurekaRegistryAvailableEvent(getEurekaServerConfig()));
// 线程状态修改为运行中
EurekaServerInitializerConfiguration.this.running = true;
// 发布 Eureka 服务端已启动事件 (暂不关注)
publish(new EurekaServerStartedEvent(getEurekaServerConfig()));
}
catch (Exception ex) {
// Help!
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);
}
... 删除部分不想解析的代码
}
从上文我所备注的信息中我们可以看到很核心的3个点
EurekaServerBootstrap
是eureka server
的启动引导类,且需要servlet方面的支持- 继承自
SmartLifecycle
后,会在所有的bean被初始化完成后会调用start方法 eurekaServerBootstrap.contextInitialized
是Eureka服务启动的核心
这里定一个传送门 Spring的SmartLifecycle调用机制
在第3点中,我们可以看到EurekaServerBootstrap
作为Eureka服务端启动引导类,那他是在哪里实例化的呢?
参照:org.springframework.cloud.netflix.eureka.server.EurekaServerAutoConfiguration#eurekaServerBootstrap
4.contextInitialized
初始化
public void contextInitialized(ServletContext context) {
try {
// 初始化 eureka环境(配置加载,不做深入)
initEurekaEnvironment();
//初始化 eureka上下文 (重点关注)
initEurekaServerContext();
// 将eureka上下文保存到servlet上下文中,便于提供管理界面数据和提供接口
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);
}
}
从上面我们可知,我们现在应该重点关注
org.springframework.cloud.netflix.eureka.server.EurekaServerBootstrap#initEurekaServerContext
方法
5. initEurekaServerContext初始化上下文
protected void initEurekaServerContext() throws Exception {
// For backward compatibility
//json解析器 添加Eureka V1版本实例信息转化器
JsonXStream.getInstance().registerConverter(new V1AwareInstanceInfoConverter(),
XStream.PRIORITY_VERY_HIGH);
//xml解析器 添加Eureka V1版本实例信息转化器
XmlXStream.getInstance().registerConverter(new V1AwareInstanceInfoConverter(),
XStream.PRIORITY_VERY_HIGH);
// 嗯,没错,这里是亚马逊特别加持对EIP和Router53的加持
if (isAws(this.applicationInfoManager.getInfo())) {
// 亚马逊绑定器
this.awsBinder = new AwsBinderDelegate(this.eurekaServerConfig,
this.eurekaClientConfig, this.registry, this.applicationInfoManager);
this.awsBinder.start();
}
// EurekaServerContext 用来提供给非spring管理的类来使用eurekaServerContext
EurekaServerContextHolder.initialize(this.serverContext);
log.info("Initialized server context");
// Copy registry from neighboring eureka node
// 获取集群中的其他节点,注册到当前节点上(重点,在后面服务端集群同步中会进行讲解)
int registryCount = this.registry.syncUp();
// 记录自身上线时间,标记自身状态为UP以便集群中其他节点进行同步,统计副本数量,开启失效实例剔除任务等
this.registry.openForTraffic(this.applicationInfoManager, registryCount);
// 注册所有监控统计信息
// Register all monitoring statistics.
EurekaMonitors.registerAllStats();
}
从上面注释信息中,我们可以看到,初始化过程为:
- 首先Eureka集群中,会进行向下兼容,即V2兼容V1版本
- 由于亚马逊路由的特殊性,所以添加亚马逊相关判断
- 对外提供非spring实例访问方式
- 同步集群其他服务端信息
- 自身信息构建,并开启实例失效剔除定时任务
- 注册各种监控指标到监控中心
到此完成Eureka的服务端启动,当然在 EurekaServerAutoConfiguration
还有很多东西我们没有讲,在那里面实例化了很多有用的东西。为了控制篇幅,我决定留在下一节继续梳理。希望能对你有帮助~