Eureka Server 启动流程分析
eureka server 启动的话,其实就相当于根据 web.xml 启动容器,然后对外提供服务,所以我们就从启动类作为入口分析
1、整体流程图
2、相应的类概念说明
2.1 ConfigurationManager
配置管理器,通过他去获取构造 Configuration
2.2 AbstractConfiguration
抽象配置类,他有很多个实现类,ConfigurationManager 构造的时候返回他,其实实例化的时候由子类来做。
- 下图是类图,可以看一下他们的关系
2.3 EurekaServerConfig
Eureka Server 的配置类,默认用 DefaultEurekaServerConfig 来实例化,主要是涵盖了 server 的相关配置,用接口的形式对外提供其配置信息
2.4 ApplicationInfoManager
应用管理器,持有 InstanceInfo、EurekaInstanceConfig 的引用,主要是对应用实例做相关的操作
2.5 EurekaClientConfig
客户端的配置,因为 eureka server 其实本身就是一个服务,他自己作为一个客户端向自己注册,如果集群的话,会向其他节点注册
2.6 DiscoveryClient
客户端组件,初始化配置,持有 ApplicationInfoManager 的引用、EurekaClientConfig的引用,还初始化了很多定时任务
2.7 PeerAwareInstanceRegistry
PeerAware,可以识别eureka server集群的:peer,多个同样的东西组成的一个集群,peers集群,peer就是集群中的一个实例
InstanceRegistry:实例注册,服务实例注册,注册表,这个里面放了所有的注册到这个eureka server上来的服务实例,就是一个服务实例的注册表
PeerAwareInstanceRegistry:可以感知eureka server集群的服务实例注册表,eureka client(作为服务实例)过来注册的注册表,而且这个注册表是可以感知到eureka server集群的。假如有一个eureka server集群的话,这里包含了其他的eureka server中的服务实例注册表的信息的。
2.8 PeerEurekaNodes
PeerEurekaNodes,代表了eureka server集群,peers大概来说多个相同的实例组成的一个集群,peer就是peers集群中的一个实例,PeerEurekaNodes,应该是代表的是eureka server集群
2.9 EurekaServerContext
Eureka Server 的上下文对象。持有了 EurekaServcerConfig、ServerCodecs、PeerAwareInstanceRegistry、PeerEurekaNodes、ApplicationInfoManager 的引用。
1、com.netflix.eureka.EurekaBootStrap
// 从类定义可以看出来,实现了 ServletContextListener,所以启动的时候就会调用 contextInitialized 方法
public class EurekaBootStrap implements ServletContextListener
public interface ServletContextListener extends EventListener {
/**
** Notification that the web application initialization
** process is starting.
** All ServletContextListeners are notified of context
** initialization before any filter or servlet in the web
** application is initialized.
*/
public void contextInitialized ( ServletContextEvent sce );
/**
** Notification that the servlet context is about to be shut down.
** All servlets and filters have been destroy()ed before any
** ServletContextListeners are notified of context
** destruction.
*/
public void contextDestroyed ( ServletContextEvent sce );
}
2、contextInitialized 方法分析
@Override
public void contextInitialized(ServletContextEvent event) {
try {
//1、初始化 eureka 环境
initEurekaEnvironment();
// 2、初始化 eureka 上下文
initEurekaServerContext();
ServletContext sc = event.getServletContext();
sc.setAttribute(EurekaServerContext.class.getName(), serverContext);
} catch (Throwable e) {
logger.error("Cannot bootstrap eureka server :", e);
throw new RuntimeException("Cannot bootstrap eureka server :", e);
}
}
3、initEurekaEnvironment 分析
protected void initEurekaEnvironment() throws Exception {
logger.info("Setting the eureka configuration..");
// 这个方法就是初始化配置类。
String dataCenter = ConfigurationManager.getConfigInstance().getString(EUREKA_DATACENTER);
if (dataCenter == null) {
logger.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);
}
String environment = ConfigurationManager.getConfigInstance().getString(EUREKA_ENVIRONMENT);
if (environment == null) {
ConfigurationManager.getConfigInstance().setProperty(ARCHAIUS_DEPLOYMENT_ENVIRONMENT, TEST);
logger.info("Eureka environment value eureka.environment is not set, defaulting to test");
}
}
public static AbstractConfiguration getConfigInstance() {
// 这里是 double check,单例模式的其中一种方法创建的时候就是这样子的,可以学习一下。
if (instance == null) {
synchronized (ConfigurationManager.class) {
if (instance == null) {
instance = getConfigInstance(Boolean.getBoolean(DynamicPropertyFactory.DISABLE_DEFAULT_CONFIG));
}
}
}
return instance;
}
private static AbstractConfiguration getConfigInstance(boolean defaultConfigDisabled) {
if (instance == null && !defaultConfigDisabled) {
// 创建默认的配置实例
instance = createDefaultConfigInstance();
// 注册配置实例
registerConfigBean();
}
return instance;
}
private static AbstractConfiguration createDefaultConfigInstance() {
// 这里就是根据配置添加各种 configuration
ConcurrentCompositeConfiguration config = new ConcurrentCompositeConfiguration();
try {
DynamicURLConfiguration defaultURLConfig = new DynamicURLConfiguration();
config.addConfiguration(defaultURLConfig, URL_CONFIG_NAME);
} catch (Throwable e) {
logger.warn("Failed to create default dynamic configuration", e);
}
if (!Boolean.getBoolean(DISABLE_DEFAULT_SYS_CONFIG)) {
SystemConfiguration sysConfig = new SystemConfiguration();
config.addConfiguration(sysConfig, SYS_CONFIG_NAME);
}
if (!Boolean.getBoolean(DISABLE_DEFAULT_ENV_CONFIG)) {
EnvironmentConfiguration envConfig = new EnvironmentConfiguration();
config.addConfiguration(envConfig, ENV_CONFIG_NAME);
}
ConcurrentCompositeConfiguration appOverrideConfig = new ConcurrentCompositeConfiguration();
config.addConfiguration(appOverrideConfig, APPLICATION_PROPERTIES);
config.setContainerConfigurationIndex(config.getIndexOfConfiguration(appOverrideConfig));
return config;
}
至此,初始化环境的方法就执行完成了,其实这个方法就是做一些准备工作,没有太多的核心逻辑。
4、initEurekaServerContext(核心逻辑,重点看一下)
protected void initEurekaServerContext() throws Exception {
//1、new 一个 EurekaServerConfig 的实例
/**
* 1、EurekaServerConfig 看一下就知道,其实他就是一个配置的类,里面各种 getXXX、shouldxxx,xxx 就是我们在 eureka-server.properties 中的一些配置
* 2、new 的时候会去初始化加载配置:ConfigurationManager.loadCascadedPropertiesFromResources(eurekaPropsFile);
* 3、其实就是去 load properties 文件,然后把这些配置设置到 Configuration 中去
*/
EurekaServerConfig eurekaServerConfig = new DefaultEurekaServerConfig();
// 2、下面就是一些 json、xml 相关的东西。这些呢主要是针对请求、响应体作一些转换、序列化操作,不用太多关注
// For backward compatibility
JsonXStream.getInstance().registerConverter(new V1AwareInstanceInfoConverter(), XStream.PRIORITY_VERY_HIGH);
XmlXStream.getInstance().registerConverter(new V1AwareInstanceInfoConverter(), XStream.PRIORITY_VERY_HIGH);
logger.info("Initializing the eureka client...");
logger.info(eurekaServerConfig.getJsonCodecName());
ServerCodecs serverCodecs = new DefaultServerCodecs(eurekaServerConfig);
ApplicationInfoManager applicationInfoManager = null;
// 3、实例化 DiscoveryClient
if (eurekaClient == null) {
// 3.1 实例化 EurekaInstanceConfig(实例配置)
EurekaInstanceConfig instanceConfig = isCloud(ConfigurationManager.getDeploymentContext())
? new CloudInstanceConfig()
: new MyDataCenterInstanceConfig();
//3.2 这里就是实例化一个 ApplicationInfoManager(持有 InstanceConfig、InstanceInfo 的引用,主要就是用于实例的一些管理操作)
applicationInfoManager = new ApplicationInfoManager(
instanceConfig, new EurekaConfigBasedInstanceInfoProvider(instanceConfig).get());
// 3.3 实例化 EurekaClientConfig(client 的配置 - eureka.client.properties 的配置、网络组件的配置- DefaultEurekaTransportConfig)
EurekaClientConfig eurekaClientConfig = new DefaultEurekaClientConfig();
// 3.4 实例化 DiscoveryClient
/**
* 1、初始化一些配置(赋值:applicationInfoManager、clientConfig、staticClientConfig、transportConfig....)
* 2、初始化线程池(注册表刷新线程池、心跳线程池、)
* 3、拉取注册表并存储(fetchRegistry)
* 4、开启定时任务(initScheduledTasks)
*/
eurekaClient = new DiscoveryClient(applicationInfoManager, eurekaClientConfig);
} else {
applicationInfoManager = eurekaClient.getApplicationInfoManager();
}
// 4、实例化 PeerAwareInstanceRegistry(初始化变量赋值、开启 DeltaRetentionTask - 主要用于过期队列中的实例信息)
PeerAwareInstanceRegistry registry;
if (isAws(applicationInfoManager.getInfo())) {
registry = new AwsInstanceRegistry(
eurekaServerConfig,
eurekaClient.getEurekaClientConfig(),
serverCodecs,
eurekaClient
);
awsBinder = new AwsBinderDelegate(eurekaServerConfig, eurekaClient.getEurekaClientConfig(), registry, applicationInfoManager);
awsBinder.start();
} else {
registry = new PeerAwareInstanceRegistryImpl(
eurekaServerConfig,
eurekaClient.getEurekaClientConfig(),
serverCodecs,
eurekaClient
);
}
// 5、实例化 PeerEurekaNodes(Eureka 节点信息)
PeerEurekaNodes peerEurekaNodes = getPeerEurekaNodes(
registry,
eurekaServerConfig,
eurekaClient.getEurekaClientConfig(),
serverCodecs,
applicationInfoManager
);
// 6、实例化 DefaultEurekaServerContext(Eureka 服务上下文)
serverContext = new DefaultEurekaServerContext(
eurekaServerConfig,
serverCodecs,
registry,
peerEurekaNodes,
applicationInfoManager
);
EurekaServerContextHolder.initialize(serverContext);
// 7、初始化 Eureka 服务上下文
/**
* 7.1、eureka 节点信息管理开启
* 7.1.1、更新 eureka 节点信息(移除不可用的,创建新的)
* 7.1.2、开启定时任务(每隔 10 分钟执行 1 次。更新 eureka 节点信息)
* 7.2、初始化 eureka-server 节点信息
* 7.2.1、集群信息定时任务开启(统计)
* 7.2.2、初始化缓存信息(二级缓存构建:readOnlyCacheMap、readWriteCacheMap - 构建的时候就指定了 180s 失效,也就是读写缓存存活时间为 180s)
* 7.2.3、开启续约阈值更新的定时任务(时间为 15 分钟)
* 7.2.4、初始化远程注册注册(主要用于从远端拉取注册表、增量拉取注册表)
*/
serverContext.initialize();
logger.info("Initialized server context");
// 8、从其他的节点 copy 注册表信息
// Copy registry from neighboring eureka node
int registryCount = registry.syncUp();
// 9、开启服务的运行
/**
* 9.1、设置服务服务的期望每分的心跳数、心跳阈值
* 9.2、开启心跳续约统计的定时任务(MeasuredRate)、过期定时任务(EvictionTask)
*/
registry.openForTraffic(applicationInfoManager, registryCount);
// 10、注册所有的监控、统计
// Register all monitoring statistics.
EurekaMonitors.registerAllStats();
}
5、总结(需要注意的点)
contextInitialized 方法执行完后,服务启动相关的主流程代码就结束了。针对上面的流程主要关注以下几点:
5.1 实例化DiscoveryClient
拉取注册表,后面我们要着重分析一下拉取注册表是一个什么样的逻辑
5.2 实例化 PeerAwareInstanceRegistryImpl
定时任务的开启,我们要关注一下这些定时任务是干啥的?他操作的数据是做啥的?比如 recentCanceledQueue、recentRegisteredQueue
5.3 serverContext.initialize()
eureka 上下文初始化(主要有 eureka server 节点信息的更新、节点更新的定时任务、初始化缓存信息-缓存干啥的怎么用、续约/过期定时任务
5.4 registry.openForTraffic
开启心跳统计的定时任务、以及心跳相关的参数配置 (这些配置跟 eureka 自身的保护机制、以及服务实例的摘除都有密切的关系,后续我们会一一分析)
版权声明:「DDKK.COM 弟弟快看,程序员编程资料站」本站文章,版权归原作者所有