前言
上篇文章已經介紹了 為何要讀netflix eureka源碼了,這里就不再概述,下面開始正式源碼解讀的內容。
如若轉載 請標明來源:一枝花算不算浪漫
代碼總覽
還記得上文中,我們通過web.xml找到了eureka server入口的類EurekaBootStrap
,這里我們就先來簡單地看下:
/**
* The class that kick starts the eureka server. 負責啟動Eureka server的類
*
* <p>
* 這里要注意兩個關鍵點:
* eureka server對應的配置類為:EurekaServerConfig
* eureka client對應的配置類為:EurekaInstanceConfig
*
* The eureka server is configured by using the configuration
* {@link EurekaServerConfig} specified by <em>eureka.server.props</em> in the
* classpath. The eureka client component is also initialized by using the
* configuration {@link EurekaInstanceConfig} specified by
* <em>eureka.client.props</em>. If the server runs in the AWS cloud, the eureka
* server binds it to the elastic ip as specified.
* </p>
*
* @author Karthik Ranganathan, Greg Kim, David Liu
*
* 負責EurekaServer初始化的類
*/
public class EurekaBootStrap implements ServletContextListener {
/**
* Initializes Eureka, including syncing up with other Eureka peers and publishing the registry.
*
* @see
* javax.servlet.ServletContextListener#contextInitialized(javax.servlet.ServletContextEvent)
*/
@Override
public void contextInitialized(ServletContextEvent event) {
try {
initEurekaEnvironment();
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);
}
}
}
看下注釋 我們可以了解到幾個關鍵點:
- eureka server對應的配置類為:EurekaServerConfig
- eureka client對應的配置類為:EurekaInstanceConfig
- EurekaBootStrap implements ServletContextListener, 所以這里會直接執行contextInitialized方法。
Eureka-Server 環境配置
初始化enviroment
接着近一步往下跟,這里可以先看 initEurekaEnvironment()
代碼如下:
protected void initEurekaEnvironment() throws Exception {
logger.info("Setting the eureka configuration..");
// 獲取dataCenter數據中心 這里重點看ConfigurationManager
// ConfigurationManager:配置管理器,管理eureka自己所有的配置,
// 重點:getConfigInstance里面使用的是volatile+synchronized+double check模式的單例模式
/**
* ConfigurationManager 創建過程:(繼續往后跟讀代碼)
* 1、創建一個ConcurrentCompositeConfiguration實例,這個類代表了所謂的配置,包括eureka需要的所有配置。
* 2、往ConcurrentCompositeConfiguration加入一堆config,然后返回ConfigurationManager實例
* 3、初始化數據中心的配置,如果沒有配置的話就是default data center
* 4、初始化eureka 運行的環境,如果沒有配置的話,默認就是test環境
*/
String dataCenter = ConfigurationManager.getConfigInstance().getString(EUREKA_DATACENTER);
// 初始化數據中心,沒有配置的話 使用DEFAULT data center
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);
}
// 獲取eureka server運行環境,沒有配置的話默認使用test環境
// 后面讀取配置文件會根據運行環境讀取,比如eureka-server-test.properties
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");
}
}
這里注釋寫的比較詳細,且這里有兩個重點:
- getConfigInstance里面使用的是volatile+synchronized+double check模式的單例模式
- ConfigurationManager 創建過程
getConfigInstance
這里一個關鍵點 是使用了很經典的double check 單例模式。
這種單例是一種線程安全的方式,里面使用了volatile+synchronized+double check,具體秒在何處 我這里就不展開講解了,搜索double check單例模式就會有很多解析文章,這里直接看代碼。
static volatile AbstractConfiguration instance = null;
/**
* Get the current system wide configuration. If there has not been set, it will return a default
* {@link ConcurrentCompositeConfiguration} which contains a SystemConfiguration from Apache Commons
* Configuration and a {@link DynamicURLConfiguration}.
*/
public static AbstractConfiguration getConfigInstance() {
if (instance == null) {
synchronized (ConfigurationManager.class) {
if (instance == null) {
instance = getConfigInstance(Boolean.getBoolean(DynamicPropertyFactory.DISABLE_DEFAULT_CONFIG));
}
}
}
return instance;
}
這里instance用volatile修飾來保證線程之間的可見性和有序性(禁止指令重排序),一般的對象創建過程都是非原子性的,內部會發生指令重排序的情況,所以加上volatile可以防止指令重排。用synchronized來保證線程串行化,double check來保證不被單例化。
接着我們就繼續往下跟,看看ConfigurationManager的創建過程。
ConfigurationManager 創建
private static AbstractConfiguration getConfigInstance(boolean defaultConfigDisabled) {
if (instance == null && !defaultConfigDisabled) {
instance = createDefaultConfigInstance();
registerConfigBean();
}
return instance;
}
private static AbstractConfiguration createDefaultConfigInstance() {
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;
}
public ConcurrentCompositeConfiguration()
{
clear();
}
public final void clear()
{
fireEvent(EVENT_CLEAR, null, null, true);
configList.clear();
namedConfigurations.clear();
// recreate the in memory configuration
containerConfiguration = new ConcurrentMapConfiguration();
containerConfiguration.setThrowExceptionOnMissing(isThrowExceptionOnMissing());
containerConfiguration.setListDelimiter(getListDelimiter());
containerConfiguration.setDelimiterParsingDisabled(isDelimiterParsingDisabled());
containerConfiguration.addConfigurationListener(eventPropagater);
configList.add(containerConfiguration);
overrideProperties = new ConcurrentMapConfiguration();
overrideProperties.setThrowExceptionOnMissing(isThrowExceptionOnMissing());
overrideProperties.setListDelimiter(getListDelimiter());
overrideProperties.setDelimiterParsingDisabled(isDelimiterParsingDisabled());
overrideProperties.addConfigurationListener(eventPropagater);
fireEvent(EVENT_CLEAR, null, null, false);
containerConfigurationChanged = false;
invalidate();
}
上面代碼是比較多,如果一行行去摳細節 真的就沒有必要了,這里我們只是看一些重點的流程,我們上面注釋也寫到過ConfigurationManager的創建過程:
1、創建一個ConcurrentCompositeConfiguration實例,這個類代表了所謂的配置,包括eureka需要的所有配置。
2、往ConcurrentCompositeConfiguration加入一堆config,然后返回ConfigurationManager實例
這里我是不建議太過於扣細節的,因為往往這些細枝末節的東西會將我們繞進去。
關於ConfigurationManager具體的細節這里也有兩篇比較好的文章推薦:
Eureka-Server 上下文加載
先看代碼:
protected void initEurekaServerContext() throws Exception {
// 1、加載eureka-server properties文件中和配置
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);
// 2、初始化一個ApplicationInfoManager,和第3步創建eureka client相關,后續會講解
ApplicationInfoManager applicationInfoManager = null;
// 3、初始化eureka-server內部的一個eureka-client(用來跟其他的eureka-server節點做注冊和通信)
// 類的開頭已經說明了:EurekaInstanceConfig其實就是eureka client相關的配置類
if (eurekaClient == null) {
EurekaInstanceConfig instanceConfig = isCloud(ConfigurationManager.getDeploymentContext())
? new CloudInstanceConfig()
: new MyDataCenterInstanceConfig();
applicationInfoManager = new ApplicationInfoManager(
instanceConfig, new EurekaConfigBasedInstanceInfoProvider(instanceConfig).get());
EurekaClientConfig eurekaClientConfig = new DefaultEurekaClientConfig();
eurekaClient = new DiscoveryClient(applicationInfoManager, eurekaClientConfig);
} else {
applicationInfoManager = eurekaClient.getApplicationInfoManager();
}
// 3、處理注冊相關的事情
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
);
}
// 4、處理peer節點相關的事情
PeerEurekaNodes peerEurekaNodes = getPeerEurekaNodes(
registry,
eurekaServerConfig,
eurekaClient.getEurekaClientConfig(),
serverCodecs,
applicationInfoManager
);
// 5、完成eureka-server上下文(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
// 6、處理一些善后的事情,從相鄰的eureka節點拷貝注冊信息
int registryCount = registry.syncUp();
registry.openForTraffic(applicationInfoManager, registryCount);
// Register all monitoring statistics.
// 7、注冊所有的監控統計項
EurekaMonitors.registerAllStats();
}
代碼有點長,加載context信息分為了上面注釋的好幾步,代碼注釋都有寫
加載eureka-server properties文件中和配置
EurekaServerConfig eurekaServerConfig = new DefaultEurekaServerConfig();
private static final DynamicStringProperty EUREKA_PROPS_FILE = DynamicPropertyFactory.getInstance().getStringProperty("eureka.server.props","eureka-server");
public DefaultEurekaServerConfig() {
init();
}
private void init() {
String env = ConfigurationManager.getConfigInstance().getString(
EUREKA_ENVIRONMENT, TEST);
ConfigurationManager.getConfigInstance().setProperty(
ARCHAIUS_DEPLOYMENT_ENVIRONMENT, env);
String eurekaPropsFile = EUREKA_PROPS_FILE.get();
try {
// ConfigurationManager
// .loadPropertiesFromResources(eurekaPropsFile);
ConfigurationManager
.loadCascadedPropertiesFromResources(eurekaPropsFile);
} catch (IOException e) {
logger.warn(
"Cannot find the properties specified : {}. This may be okay if there are other environment "
+ "specific properties or the configuration is installed with a different mechanism.",
eurekaPropsFile);
}
}
public static void loadCascadedPropertiesFromResources(String configName) throws IOException {
Properties props = loadCascadedProperties(configName);
if (instance instanceof AggregatedConfiguration) {
ConcurrentMapConfiguration config = new ConcurrentMapConfiguration();
config.loadProperties(props);
((AggregatedConfiguration) instance).addConfiguration(config, configName);
} else {
ConfigurationUtils.loadProperties(props, instance);
}
}
首先我們看下EurekaServerConfig
:
里面包含好多getxxx方法,看一下具體實現:
其中configInstance是DynamicPropertyFactory
對象。EurekaServerConfig,這是個接口,這里面有一堆getXXX()的方法,包含了eureka server需要使用的所有的配置,都可以通過這個接口來獲取。
想象一下,eureka-sever.properties文件里,都是一個一個的key=value的很多的配置項,肯定是將這些key-value格式的配置項加載到內存的Properties對象去存放,Map。一般來說,如果讓我們自己來設計這個讀取properties文件的配置的代碼,也許我們就是做到將配置加載到Properties對象中就結束了。
EurekaServerConfig,代表了eureka-server需要的所有的配置項,通過接口定義了大量的方法,讓你可以從這里獲取所有你需要的配置
DefaultEurekaServerConfig
就是上面EurekaServerConfig
的實現類,創建實例的時候,會執行一個init()方法,在這個方法中,就會完成eureka-server.properties文件中的配置項的加載。EUREKA_PROPS_FILE,對應着要加載的eureka的配置文件的名字。
將加載出來的Properties中的配置項都放到ConfigurationManager中去,由這個ConfigurationManager來管理
比如說eureka-server那個工程里,就有一個src/main/resources/eureka-server.properties文件,只不過里面是空的,全部都用了默認的配置
DefaultEurekaServerConfig.init()方法中,會將eureka-server.properties文件中的配置加載出來,都放到ConfdigurationManager中去,然后在DefaultEurekaServerConfig的各種獲取配置項的方法中,配置項的名字是在各個方硬編碼的,是從一個DynamicPropertyFactory里面去獲取的,你可以認為DynamicPropertyFactory是從ConfigurationManager那兒來的,因為ConfigurationManager中都包含了加載出來的配置了,所以DynamicPropertyFactory里,也可以獲取到所有的配置項
在從DynamicPropertyFactory中獲取配置項的時候,如果你沒配置,那么就用默認值,全部都給你弄好了各個配置項的默認值,相當於所有的配置項的默認值,在DefaultEurekaServerConfig的各個方法中,都可以看到,如果你沒配置,那么就用這里的默認值就可以了
加載eureka-server.properties的過程:
(1)創建了一個DefaultEurekaServerConfig對象
(2)創建DefaultEurekaServerConfig對象的時候,在里面會有一個init方法
(3)先是將eureka-server.properties中的配置加載到了一個Properties對象中,然后將Properties對象中的配置放到ConfigurationManager中去,此時ConfigurationManager中去就有了所有的配置了
(4)然后DefaultEurekaServerConfig提供的獲取配置項的各個方法,都是通過硬編碼的配置項名稱,從DynamicPropertyFactory中獲取配置項的值,DynamicPropertyFactory是從ConfigurationManager那兒來的,所以也包含了所有配置項的值
(5)在獲取配置項的時候,如果沒有配置,那么就會有默認的值,全部屬性都是有默認值的
申明
本文章首發自本人博客:https://www.cnblogs.com/wang-meng 和公眾號:壹枝花算不算浪漫,如若轉載請標明來源!
感興趣的小伙伴可關注個人公眾號:壹枝花算不算浪漫