开局来一张eureka server的启动流程图:
首先,github上下载eureka源码,找到eureka-server这个module,找到web.xml,对于Servlet的监听器Listener,它是实现了javax.servlet.ServletContextListener 接口的服务器端程序,它也是随web应用的启动而启动,只初始化一次,随web应用的停止而销毁。
<!-- listener:项目启动入口,入口类:EurekaBootStrap-->
<listener>
<listener-class>com.netflix.eureka.EurekaBootStrap</listener-class>
</listener>
那么整个项目的启动类就是com.netflix.eureka.EurekaBootStrap这个类。来看类定义:
public class EurekaBootStrap implements ServletContextListener {
实现该接口,就要实现contextInitialized这个方法。
/**
** 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 );
其实现方法是:
@Override
public void contextInitialized(ServletContextEvent event) {
try {
//初始化环境
initEurekaEnvironment();
//启动eureka server
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);
}
}
主要就是两个方法,一个是initEurekaEnvironment();一个是initEurekaServerContext();
首先来看第一个initEurekaEnvironment()方法:
protected void initEurekaEnvironment() throws Exception {
logger.info("Setting the eureka configuration..");
//从ConfigurationManager拿到一个配置管理器实例
//eureka.datacenter:从eureka-client.properties中读取,默认为空
String dataCenter = ConfigurationManager.getConfigInstance().getString(EUREKA_DATACENTER);
if (dataCenter == null) {
//这里是null,dataCenter被设为default,datacenter应该是在多个数据中心用来区分环境的
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);
}
//这里的EUREKA_ENVIRONMENT用来觉定读取哪个配置文件,将会拼接到配置文件名称上,拼接后的形式是:
// eureka-server-<eureka.environment>.properties
//EUREKA_ENVIRONMENT配置:eureka.environment
String environment = ConfigurationManager.getConfigInstance().getString(EUREKA_ENVIRONMENT);
if (environment == null) {
//ARCHAIUS_DEPLOYMENT_ENVIRONMENT:archaius.deployment.environment,默认设置为TEST:test
ConfigurationManager.getConfigInstance().setProperty(ARCHAIUS_DEPLOYMENT_ENVIRONMENT, TEST);
logger.info("Eureka environment value eureka.environment is not set, defaulting to test");
}
}
重点看第二个方法initEurekaServerContext():
/**
* init hook for server context. Override for custom logic.
*/
protected void initEurekaServerContext() throws Exception {
//通过配置文件拿配置保存在内存中
EurekaServerConfig eurekaServerConfig = new DefaultEurekaServerConfig();
// 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;
//初始化ApplicationInfoManager 初始化eurekaclient,这里显然会走if语句
if (eurekaClient == null) {
//不是云环境,走MyDataCenterInstanceConfig,通过eureka-client.properties读取配置
EurekaInstanceConfig instanceConfig = isCloud(ConfigurationManager.getDeploymentContext())
? new CloudInstanceConfig()
: new MyDataCenterInstanceConfig();
//new EurekaConfigBasedInstanceInfoProvider(instanceConfig).get() 返回 InstanceInfo
//基于配置和instanceInfo作为服务实例的一个管理器
applicationInfoManager = new ApplicationInfoManager(
instanceConfig, new EurekaConfigBasedInstanceInfoProvider(instanceConfig).get());
//该实例代表了读取的eureka-client.properties配置文件
EurekaClientConfig eurekaClientConfig = new DefaultEurekaClientConfig();
//构建eurekaClient
eurekaClient = new DiscoveryClient(applicationInfoManager, eurekaClientConfig);
} else {
applicationInfoManager = eurekaClient.getApplicationInfoManager();
}
//初始化PeerAwareInstanceRegistry
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 {
// 走这里,创建PeerAwareInstanceRegistryImpl:集群中的一个server实例
//也就是当前的这个server实例
registry = new PeerAwareInstanceRegistryImpl(
eurekaServerConfig,
eurekaClient.getEurekaClientConfig(),
serverCodecs,
eurekaClient
);
}
// 代表了整个eureka server的集群,创建了peerEurekaNodes
PeerEurekaNodes peerEurekaNodes = getPeerEurekaNodes(
registry,
eurekaServerConfig,
eurekaClient.getEurekaClientConfig(),
serverCodecs,
applicationInfoManager
);
//将构造好的对象用来创建EurekaServerContext,context用于在整个系统运行期间
// 可以通过context获取变量
serverContext = new DefaultEurekaServerContext(
eurekaServerConfig,
serverCodecs,
registry,
peerEurekaNodes,
applicationInfoManager
);
EurekaServerContextHolder.initialize(serverContext);
serverContext.initialize();
logger.info("Initialized server context");
// Copy registry from neighboring eureka node
//从相邻的一个server拷贝注册表信息
int registryCount = registry.syncUp();
registry.openForTraffic(applicationInfoManager, registryCount);
// Register all monitoring statistics.
EurekaMonitors.registerAllStats();
}
这个方法是启动时最重要的方法了,这个方法执行完成,eureka server就启动完成了,后面文章将详细分析启动具体过程。
总结一下:
web.xml文件
->Listener类EurekaBootStrap
->contextInitialized方法
->initEurekaEnvironment()
->initEurekaServerContext();
后面重点分析initEurekaServerContext();方法。